java.svg

【Java】Listの基本

Java
2025/01/05

List の宣言

Listはインターフェースのため、ArrayListなどの実装クラスを使用します。

他にもLinkedListVectorなどの実装クラスはありますが、多くの場合はArrayListを使用します。

この記事でもArrayListを中心に説明していきます。

Listを使用する際は、ジェネリスク<>を用いて要素のクラスを指定します。 intなど基本のデータ型を使用する際は、Integerのようなラッパークラスを用います。

List<Integer> list = new ArrayList<Integer>();

右辺のジェネリクスは省略することができます。

List<Integer> list = new ArrayList<>();

また以下のようにして配列をListに変換することができます。

String[] ary = {"AAA", "BBB", "CCC"};
List<String> list = Arrays.ofList(ary);

List の追加

add

Listに要素を追加するにはaddを使用します。

List<String> list = new ArrayList<>();
list.add("AAA");
list.add("BBB");
list.add(1, "CCC");

//結果
//["AAA", "CCC", "BBB"]

引数が 1 つの場合は、その要素を末尾に追加します。 引数が 2 つの場合は、指定したインデックスに要素を追加します。

addAll

addは 1 つの要素を追加しますが、addAllは複数の要素を追加します。

List<String> list = new ArrayList<>();
list.add("AAA");
list.add("BBB");

List<String> add1 = new ArrayList<>();
add1.add("CCC");
add1.add("DDD");

List<String> add2 = new ArrayList<>();
add2.add("EEE");
add2.add("FFF");

list.addAll(add1);
list.addAll(1, add2);

//結果
//["AAA", "EEE", "FFF", "BBB", "CCC", "DDD"]

addと同じく引数 1 つの場合は末尾に追加し、2 つの場合は指定したインデックスから追加します。

List の参照

get

getは引数に指定したインデックスの要素を取得します。

List<String> list = new ArrayList<>();
list.add("AAA");
list.add("BBB");
list.add("CCC");

list.get(1);  //"BBB"

要素数範囲外のインデックスを指定した場合はIndexOutOfBoundsExceptionが発生します。

size

sizeListの要素数を返します。

List<String> list = new ArrayList<>();
list.add("AAA");
list.add("BBB");
list.add("CCC");

list.size();  //3

isEmpty

isEmptyは、Listの要素数が 0 であることを判定し、その結果を真偽値で返します。

List<String> list = new ArrayList<>();

list.isEmpty();  //true

list.add("AAA");

list.isEmpty();  //false

List の置換

set

setは、指定したインデックスの要素を置き換えに使用します。

List<String> list = new ArrayList<>();
list.add("AAA");
list.add("BBB");
list.add("CCC");

list.set(1, "DDD");

//結果
//["AAA", "DDD", "CCC"]

replaceAll

replaceAllListのすべての要素に対し、何らかの変更を加える際に使用します。

例えばすべての要素を小文字に変更する場合は以下のようにします。

List<String> list = new ArrayList<>();
list.add("AAA");
list.add("BBB");
list.add("CCC");

list.replaceAll(String::toLowerCase);

//結果
//["aaa", "bbb", "ccc"]

詳細は割愛しますが、引数に指定するのは関数型インターフェースUnaryOperator<T>になります。 要素に対し、変換がかけられない場合はUnsupportedOperationExceptionが発生します。

List の検索

indexOf, lastIndexOf

indexOflastIndexOfは引数の要素を検索し、最初に一致した要素のインデックスを返します。 対象の要素がない場合は-1を返します。

要素の一致とはequalstrueのものを指します。

indexOfは先頭から検索し、lastIndexOfは末尾から検索します。

List<String> list = new ArrayList<>();
list.add("AAA");
list.add("BBB");
list.add("CCC");
list.add("BBB");
list.add("CCC");
list.add("AAA");

list.indexOf("BBB");  //1
list.lastIndexOf("BBB"); //3

contains, containsAll

contains, containsAllは引数の要素を検索し、一致する要素があるかを真偽値で返します。

containsは引数の 1 つの要素を検索し、containsAllは引数のすべての要素を検索し、すべての要素に対して一致する要素がある場合のみtrueを返します。

List<String> list = new ArrayList<>();
list.add("AAA");
list.add("BBB");
list.add("CCC");

List<String> listA = new ArrayList<>();
listA.add("BBB");
listA.add("CCC");

List<String> listB = new ArrayList<>();
listB.add("BBB");
listB.add("DDD");

list.contains("BBB");     //true
list.contains("DDD");     //false
list.containsAll(listA);  //true
list.containsAll(listB);  //false

List の削除

clear

clearListの要素をすべて削除し、空の状態(要素数を 0)にします。

remove

removeは指定した要素を削除するのですが、指定方法が 2 パターンあります。

1 つはインデックスを指定する方法です。そのままインデックスに該当する要素を削除します。

もう 1 つはインスタンスを指定する方法です。検索の場合と同じで、引数に指定したインスタンスと一致する最初の要素を削除します。

List<String> list = new ArrayList<>();
list.add("AAA");
list.add("BBB");
list.add("CCC");
list.add("DDD");
list.add("EEE");

list.remove(1);       //["AAA", "CCC", "DDD", "EEE"]
list.remove("DDD");   //["AAA", "CCC", "EEE"]

removeAll

removeAllは引数のすべての要素に一致する要素を削除します。

List<String> list = new ArrayList<>();
list.add("AAA");
list.add("BBB");
list.add("CCC");
list.add("AAA");

List<String> listA = new ArrayList<>();
listA.add("AAA");
listA.add("CCC");

list.removeAll(listA);  //["BBB"]

ArrayListについて

最後にArrayListについて補足しておきます。

内部構造

ArrayListは内部ではObject[]という配列に要素を格納されます。 配列のサイズは、コンストラクタの引数で指定でき、デフォルトでは10となっています。

List<Integer> list1 = new ArrayList<>();   //10
List<Integer> list2 = new ArrayList<>(10000);  //10000

ここで配列のサイズイコールsizeメソッドで取得できる値ではない点に注意してください。 配列のサイズを直接調べるメソッドは用意されていません。

要素の追加

addメソッドにより要素を追加しますが、配列のサイズとの関係によって少し挙動が異なります。

配列のサイズに空きがある場合、末尾に要素を追加するだけです。 しかし、配列のサイズに空きがない場合は配列のサイズを大きくする必要があります。 このために、新しいサイズの配列を作成し、元の配列の要素をコピーします。

計算量でみると、空きがある場合はO(1)ですが、空きがない場合は要素分コピーを行うためO(n)となります。

そのため、大量データを扱うことがわかっている場合は、初期時点での配列サイズは大きく指定した方がパフォーマンスはよくなる場合があります。

要素の参照

ArrayListでは前述の通り配列を使用しているため、インデックスを指定した要素へのアクセス(ランダムアクセス)はO(1)と高速です。 つまり、getsetメソッドは高速に参照・置換が可能です。

しかし、indexOfメソッドで要素自体を検索する場合は、順に要素を比較して対象のインデックスを検索するため、最大O(n)の計算量となります。

要素の削除

要素を削除する場合、削除された要素以降の要素を左にシフトする処理が行われます。 そのため最大O(n)の計算量となります。

しかし、これはあくまでインデックスを指定した場合の話であり、要素自体を指定する場合はindexOfメソッド同様に順に検索する処理が必要となります。

メモリについて

要素の追加で記述した通り、配列サイズに空きがない場合は、新しいサイズの配列を作成します。 この新しいサイズは、元のサイズの約1.5倍となります。 つまり、だんだんと追加されるサイズも大きくなっていきます。

こうなったときに、無駄にメモリ領域が使用される可能性があります。 例えば、追加で10,000増やしたのに1要素しか追加しなかったので9,999分余ってしまう、といった感じです。

このような場合はtrimToSizeメソッドを使用します。 これにより、配列のサイズを要素数に合わせてくれます。

ArrayList<Integer> list = new ArrayList<>(10000);
list.add(1);
list.add(2);

list.trimToSize();

特に大量データを扱う場合には無駄になることが多いため、trimToSizeメソッドの使用をお勧めします。

ArrayListのまとめ

ArrayListは内部的に配列を用いていることから、インデックスを用いた要素の参照については非常に高速です。 しかし、要素の追加や削除については時間がかかる場合があるためあまり向いていません。 追加・削除が多い場合はLinkedListなどを使用することを検討します。