java.util.List#subListの誤った用法

java.util.List#subListメソッドの使い方がまちがっていたためにメモリリークを犯してしまっていた、という話です。


画像の各ピクセルの色を抽出してある法則に並べ替えた後に、先頭の10個の要素を返す、というメソッドを作るとしましょう。
このとき

List<Color> cs = ...
Collections.sort(cs, COMPARATOR);
return cs.subList(0, 10);

と書いてしまうと問題が生じます。csがメモリリークを起こすのです。


java.util.List#subListのJavadocによると、このメソッドはあるリストの特定の範囲を操作したいときの面倒なインデックス計算を省略するためにあるようです。
例えばJavadocの例ですが

list.subList(from, to).clear();

と書くとのfromからtoの範囲の要素を削除できます。

ここで重要なのは、subListメソッドが返すListオブジェクトを操作することで元のListオブジェクト(この例だとlist変数に格納されたListオブジェクト)が変更されている、ということです。
つまり、subListメソッドが返すListオブジェクトは元のListオブジェクトを参照しているということになります。


これを踏まえて冒頭に挙げたコードを見てみます。

List<Color> cs = ...
Collections.sort(cs, COMPARATOR);
return cs.subList(0, 10);

cs変数が参照しているListオブジェクトはメソッドから抜けると不要になるにかかわらず、subListメソッドが返すListオブジェクトからずっと参照され続けるので、メモリリークを起こすわけですね。

これを防ぐには

List<Color> cs = ...
Collections.sort(cs, COMPARATOR);
return new ArrayList<Color>(cs.subList(0, 10));

と書く必要があります。


Javaのコレクションの中でも最も出番の多いであろうjava.util.Listですが、油断すると落とし穴にはまりますね。。。