例えば、sRGB の10進定数をフィールドに持つ Item があったとして、
public static int RED = Integer.parseInt("FF0000", 16); public static int GREEN = Integer.parseInt("00FF00", 16); public static int BLUE = Integer.parseInt("0000FF", 16); private record Item(int colorDec, String name) { }
この Item を色別にグルーピングして map に格納する。
private Map<Integer, List<Item>> map = new HashMap<>(); public void put(Item item) { map.computeIfAbsent(item.color, ArrayList::new).add(item); }
computeIfAbsent
にて、キーが存在しない場合は、ArrayList::new
で新しいリスト作成して追加。
でも、これは間違いで、やってしまいがち。 見た目も正しそうに見え、しかも正しく動作するので厄介。
メソッド参照を書き下すと、以下のようになり、
map.computeIfAbsent(item.color, (Integer i) -> new ArrayList(i)).add(item);
new ArrayList(int initialCapacity)
はリストの初期容量を指定するコンストラクタで、以下のように配列が確保される。
transient Object[] elementData; public ArrayList(int initialCapacity) { ... this.elementData = new Object[initialCapacity]; }
上記例では new Object[16711680]
と、不要な大量メモリが確保されてしまうこととなる。
なので、以下のように引数なしのコンストラクタを呼ぶのが正しい。
public void put(Item item) { map.computeIfAbsent(item.color, i -> new ArrayList()).add(item); }
キーが Integer 以外のオブジェクトであれば、コンパイルエラーで気付くことができるが、キーが Integer の場合、意図しないコンストラクタ呼び出しとなり、しかもArrayList内で確保された容量は、ArrayList内でカプセル化され、外部からは知り得ない(リフレクション除く)ので、気付くことはほぼできない。
ので用心が必要。