今更な感じでもあるけど、ちょっと疑問に思ったことをAIチャットに聞いて、ソースを確認して理解したメモ。
int型の数値からInteger型にオートボクシングする際にnew Integer(v)なのかInteger.valueOf(v)なのか
Javaのオートボクシング(Autoboxing)において、プリミティブ型が対応するラッパークラス型に暗黙的に変換される場合、コンパイラは通常、Integer.valueOf(v) を使用します。
Integer.valueOf(v) の利点 (キャッシュ)
Integer.valueOf(int i) メソッドは、Javaの仕様上、-128から127までの範囲の整数に対して**キャッシュ(Pool)**を利用することが保証されています。
- キャッシュ範囲内の値(-128 $\le v \le$ 127): 既に生成された同じ値の Integer オブジェクトが返されます。これにより、新しいオブジェクトの生成が抑制され、メモリの節約と処理速度の向上につながります。
- キャッシュ範囲外の値: 通常通り、新しい Integer オブジェクトが生成されます(new Integer(v) とほぼ同じ動作になります)。
→Integerのソースを確認して、内部クラスIntegerCacheがあり、low = -128は固定、high = 127がデフォルトで、VM.getSavedProperty("java.lang.Integer.IntegerCache.high")でカスタマイズできる。
❌ new Integer(v) の使用は非推奨
new Integer(int v) コンストラクタは、Java 9以降で**非推奨(Deprecated)**となり、将来のバージョンで削除される予定です。これは、上記のキャッシュの利点がないため、常に新しいオブジェクトを生成してしまい、非効率であるためです。
→実は順番が逆で、new Integer(v)が非推奨となっていることに気が付いて、わざわざnew Integer(v)などと書かないけど、自動でint型から変換されるときにどうなるのかなぁと。
ArrayList< E>にremove(int idx)とremove(Object obj)があるが、E=Integerの場合に区別が難しい
汎用的なコレクション設計の優先
ArrayList は、ジェネリクス () を使用して設計されており、E が Integer だけでなく、String、Person、カスタムクラスなど、あらゆる型を取り扱うことができます。
- E が String や Person の場合、list.remove("hoge") や list.remove(personObject) のように、remove(Object) は直感的かつ自然に使えます。
- Integer で問題が起きるのは、プリミティブ型 int がそのままインデックスとして利用されるため、オートボクシング(int $\to$ Integer)とインデックス指定(int)の曖昧さが発生する特殊なケースであると見なされます。
Java 1.2 時代の設計の影響
ArrayList のベースとなる List インターフェースはJava 1.2で導入されました。この時代は、ジェネリクス(List)がまだ存在せず、コレクションはすべて Object を扱っていました(Raw Type)。
- 当時から、コレクションが「要素オブジェクト」を受け取るメソッドは、引数名を Object o として、メソッド名を remove(Object) とするのが一般的でした。
- ジェネリクスが導入された後も、後方互換性とAPIの一貫性を保つため、この命名規則が維持されました。
→確かにJava 1.4以前のときは、StringやPersonをListに使うことがあっても、わざわざIntegerをListにしないで、int[]で書いていたわな。add()はいいけど、get()の戻り値がObjectなので、常に目的のクラスにダウンキャスト。