地味にハマってしまったので。
はじめに
Javaでリストを扱う時、よくArrayListをnewして使うと思います。
List<String> list = new ArrayList<>();
そのArrayListを使い回したいという時にGoogle先生に聞いてみると、
- newする
- ArrayList.clear()メソッドを使う
の2通りをオススメされると思います。そして、両者の違いについては特に触れられず、強いて言えば「実行速度が違うよ」と言っていくらかQiitaの記事がヒットすると思います。
しかし、実際のところは速度だけではなく、挙動にも異なる点があります。
newとclear()の違い
1次元リストから要素を2つずつ取り出して2次元リストを作る場合を想定します。
以下にサンプルコードを示します。
まずはnewした場合からです。
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class TestArraylist {
public static void main(String args[]) {
// 出力用の2次元リスト
List<ArrayList<String>> sampleLists = new ArrayList<>();
// sampleListsに要素を追加するための一時的なリスト
ArrayList<String> listA = new ArrayList<>();
// 適当なサンプル
List<String> listB = Arrays.asList("AA", "BB", "CC", "DD", "EE", "FF");
// 要素数が2のリストを作ってsampleListsに追加する
for (int i = 0; i < listB.size(); i++) {
if (i % 2 == 0) {
listA.add(listB.get(i));
continue;
}
sampleLists.add(listA);
listA.add(listB.get(i));
// new前の確認
System.out.println(sampleLists);
// 一時的なリストをnewする
listA = new ArrayList<>();
// new後の確認
System.out.println(sampleLists);
}
}
}
これを実行すると・・・
[[AA, BB]]
[[AA, BB]]
[[AA, BB], [CC, DD]]
[[AA, BB], [CC, DD]]
[[AA, BB], [CC, DD], [EE, FF]]
[[AA, BB], [CC, DD], [EE, FF]]
ちゃんと目的通りのリストが出来上がっていることがわかります。
一方、newの代わりにclearメソッドを使ったとします。上記サンプルのlistA = new ArrayList<>();
をclearメソッドを用いたコードlistA.clear();
に書き換えた時の出力は・・・
[[AA, BB]]
[[]]
[[CC, DD], [CC, DD]]
[[], []]
[[EE, FF], [EE, FF], [EE, FF]]
[[], [], []]
このように、確かに追加したはずの要素がclearメソッドを呼び出すたびに要素0のリストとなってしまい、次にclearメソッドを呼び出すまで新たに追加した要素がすべてのリストに追加されてしまいます。
おわりに
ググってみると配列の要素を消すのはどちらを使ってもよいというページがたくさん出てくるので油断してしまいますが、実際は上記のような異なる挙動をしているので注意が必要です。