HOWTO · Java

Java の ArrayList から最後の要素を取得する

Java の ArrayList から最後の要素を取得する方法、空のリストへの対処、末尾へのアクセスを繰り返す場合に適した代替手段を解説します。

このページの内容

Java の ArrayList から最後の要素を取得するには、list.get(list.size() - 1) の位置にある要素を読み取ります。Java のインデックスは 0 から始まるため、最後の有効なインデックスはリストのサイズより 1 小さい値です。Python のリストとは異なり、Java のリストでは最後の要素を示す -1 のような負のインデックスは使えません。

size() と get() で最後の要素を取得する

size() でリストの要素数を調べ、1 を引いて最後のインデックスを求め、そのインデックスを get() に渡します。たとえば、要素が 4 つあるリストの有効なインデックスは 0 から 3 なので、最後の要素のインデックスは 4 - 1 です。

次の例を実行します。

このセクションのコードと表示されるラベルや出力は英語のままです。

import java.util.ArrayList;

public class LastElement {
    public static void main(String[] args) {
        ArrayList<Integer> numbers = new ArrayList<>();
        numbers.add(10);
        numbers.add(20);
        numbers.add(30);
        numbers.add(40);

        int last = numbers.get(numbers.size() - 1);
        System.out.println(last);
    }
}
40

この直接参照は、値が ArrayList で、その最後の要素が必要な場合に一般的に使われる方法です。ストリームやコピーしたサブリストを使う必要も、別のコレクションへ変換する必要もありません。

空の ArrayList を処理する

空のリストのサイズは 0 なので、size() - 1 は -1 になります。そのインデックスを get() に渡すと、IndexOutOfBoundsException がスローされます。要素にアクセスする前にリストを確認し、呼び出し元の要件に合った結果を選んでください。

「要素がない」ことを null で表すメソッドでは、その契約を明確にします。このヘルパーは、null のリストを空として扱わず、拒否します。

このセクションのコードと表示されるラベルや出力は英語のままです。

import java.util.ArrayList;

public class LastElement {
    static <T> T lastOrNull(ArrayList<T> list) {
        if (list == null) {
            throw new IllegalArgumentException("list must not be null");
        }
        return list.isEmpty() ? null : list.get(list.size() - 1);
    }

    public static void main(String[] args) {
        ArrayList<String> names = new ArrayList<>();
        String last = lastOrNull(names);
        System.out.println(last == null ? "No elements" : last);
    }
}
No elements

null を返す方法は簡単ですが、呼び出し元は「要素がない」場合と、リスト内に正当に含まれる null 値とを区別する必要があります。その区別が重要なら、代わりに Optional<T> を返すか、get() を呼び出す前に呼び出し元で空リストを処理してください。空の入力をどう扱うか決める代わりに、IndexOutOfBoundsException を捕捉するのは避けましょう。

末尾へのアクセスを繰り返す場合のコレクションを選ぶ

1 回だけ参照するために ArrayList を ArrayDeque または LinkedList に変換しても、元のリストに利点はなく、メモリ確保と要素のコピーが発生します。ArrayList のまま、最後のインデックスを使いましょう。

両端で要素の追加と削除を繰り返すプログラムでは、最初からそのアクセスパターンに合った deque を選びます。ArrayDeque には addLast() や getLast() などの末尾操作があります。LinkedList にも getLast() がありますが、格納方法やメモリ特性は異なります。これらはコレクションを設計するときに検討する選択肢であり、1 回だけ読み取るための近道ではありません。ArrayDeque は null 要素を許可しないため、データモデルに null 値が含まれる場合はその点も考慮してください。

Guava の Iterables.getLast() を使う

プロジェクトがすでに Guava に依存している場合、Iterables.getLast(iterable) は Iterable に対する任意の代替手段です。Iterables.getLast() の公式 Guava API ドキュメントには、2 つのオーバーロードが記載されています。引数が 1 つのメソッドは iterable が空の場合に NoSuchElementException をスローし、Iterables.getLast(iterable, defaultValue) は要素がない場合に指定された既定値を返します。

この方法にはプロジェクトのクラスパス上に Guava が必要で、Java 標準ライブラリには含まれていません。ほかに Guava を使う理由がない ArrayList では、size() と get() を使えば依存関係を追加せずに済みます。

まとめ

空でない ArrayList の最後の要素を取得するには、list.get(list.size() - 1) を使います。空のリストではインデックスが -1 になり、get() は IndexOutOfBoundsException をスローするため、先に空かどうかを確認し、要素がない場合の扱いを明確にしてください。1 回の参照のためにリストを変換するのは避け、両端での操作を繰り返す用途では ArrayDeque など別のコレクションを検討します。すでに適切な依存関係がある場合は、Guava の Iterables.getLast() も利用できます。