Java Language
オートボクシング
サーチ…
前書き
Autoboxingは、Javaコンパイラがプリミティブ型とそれに対応するオブジェクトラッパークラスの間で行う自動変換です。例、int - > Integer、double - > Double ...の変換変換が逆の場合は、これをunboxingと呼びます。通常、これは、コレクション以外のオブジェクトを保持できないコレクションで使用されます。ボクシングプリミティブ型はコレクション内で設定する前に必要です。
備考
オートボクシングでは、コードで頻繁に使用するとパフォーマンスの問題が発生する可能性があります。
intとIntegerを同じ意味で使用する
ユーティリティクラスでジェネリック型を使用する場合、オブジェクト型として指定されている場合、それらのプリミティブ型と等しくないので、数値型はそれほど役に立ちません。
List<Integer> ints = new ArrayList<Integer>();
List<Integer> ints = new ArrayList<>();
幸いなことに、して評価する式int
の代わりに使用することができるInteger
が必要なとき。
for (int i = 0; i < 10; i++)
ints.add(i);
ints.add(i);
ステートメントは次のものと同等です。
ints.add(Integer.valueOf(i));
また、 Integer#valueOf
プロパティは、数値キャッシュの範囲内にあるときにJVMによってキャッシュされる同じInteger
オブジェクトを保持するなど、保持されます。
これは以下の場合にも適用されます:
-
byte
とByte
-
short
とShort
-
float
とFloat
-
double
とDouble
-
long
とLong
-
char
とCharacter
-
boolean
とBoolean
しかし、あいまいな状況では注意が必要です。次のコードを考えてみましょう:
List<Integer> ints = new ArrayList<Integer>();
ints.add(1);
ints.add(2);
ints.add(3);
ints.remove(1); // ints is now [1, 3]
java.util.List
インタフェースには、 remove(int index)
( List
インタフェースメソッド)とremove(Object o)
( java.util.Collection
から継承されたメソッド)の両方が含まれています。この場合、ボクシングは行われず、 remove(int index)
が呼び出されます。
範囲の値を持つ整数をオートボクシングにより生じる奇妙なJavaコードの動作の一の以上の実施例-128
と127
:
Integer a = 127;
Integer b = 127;
Integer c = 128;
Integer d = 128;
System.out.println(a == b); // true
System.out.println(c <= d); // true
System.out.println(c >= d); // true
System.out.println(c == d); // false
これが起こる>=
演算子は、暗黙的に呼び出しintValue()
戻りint
ながら==
参照ではなく、比較int
値。
デフォルトでは、Javaの値は[-128, 127]
範囲でキャッシュされるため、演算子==
機能するのは、この範囲のIntegers
は、値が同じ場合に同じオブジェクトを参照するためです。キャッシング可能範囲の最大値は、 -XX:AutoBoxCacheMax
JVMオプションを使用して定義できます。つまり、 -XX:AutoBoxCacheMax=1000
でプログラムを実行すると、次のコードはtrue
。
Integer a = 1000;
Integer b = 1000;
System.out.println(a == b); // true
if文のブール値の使用
自動アンボックスのため、 if
文でBoolean
を使用できます。
Boolean a = Boolean.TRUE;
if (a) { // a gets converted to boolean
System.out.println("It works!");
}
それはのために働くwhile
、 do while
とにおける条件for
にも記述が含まれます。
Boolean
がnull
場合、変換でNullPointerException
がスローされることに注意してください。
自動アンボクシングによってNullPointerExceptionが発生することがある
このコードをコンパイルする:
Integer arg = null;
int x = arg;
しかし、実行時に2行目のjava.lang.NullPointerException
でクラッシュします。
問題は、プリミティブint
はnull
値を持つことができないということです。
これは最小限の例ですが、実際にはより洗練された形でよく現れます。 NullPointerException
は非常に直感的ではなく、しばしばそのようなバグの特定に役立つことはほとんどありません。
autoboxingとauto-unboxingに注意して、実行時にボックス化されていない値にnull
値がないことを確認してください。
オートボクシングのメモリと計算上のオーバーヘッド
オートボクシングはかなりのメモリオーバーヘッドになる可能性があります。例えば:
Map<Integer, Integer> square = new HashMap<Integer, Integer>();
for(int i = 256; i < 1024; i++) {
square.put(i, i * i); // Autoboxing of large integers
}
相当量のメモリを消費します(6kの実際のデータで約60kb)。
さらに、ボックス化された整数は、通常、メモリに追加のラウンドトリップを必要とし、CPUキャッシュの効率を低下させます。上記の例では、アクセスされるメモリは、メモリの全く異なる領域にある5つの異なる場所、 HashMap
オブジェクト、2。マップのEntry[] table
オブジェクト、 Entry
オブジェクト、4。エントリkey
オブジェクト(ボクシングプリミティブキー)、5.エントリvalue
オブジェクト(ボクシングプリミティブ値)。
class Example {
int primitive; // Stored directly in the class `Example`
Integer boxed; // Reference to another memory location
}
読書boxed
はprimitive
のみにアクセスする2つのメモリアクセスを必要とする。
このマップからデータを取得すると、一見無邪気なコード
int sumOfSquares = 0;
for(int i = 256; i < 1024; i++) {
sumOfSquares += square.get(i);
}
次のものと同等です。
int sumOfSquares = 0;
for(int i = 256; i < 1024; i++) {
sumOfSquares += square.get(Integer.valueOf(i)).intValue();
}
通常、上記のコードは、 Map#get(Integer)
操作ごとにInteger
オブジェクトの作成とガベージコレクションを行います。 (詳細は下記の注を参照してください)。
このオーバーヘッドを減らすために、いくつかのライブラリは 、ボクシングを必要としないプリミティブ型の最適化されたコレクションを提供します。ボクシングのオーバーヘッドを避けることに加えて、これらのコレクションではエントリあたり約4倍のメモリが必要になります。 Javaホットスポットは 、ヒープではなくスタック上のオブジェクトを操作することでオートボクシングを最適化できる可能性がありますが、メモリのオーバーヘッドとその結果のメモリ間接参照を最適化することはできません。
Java 8ストリームには、ボクシングを必要としないIntStream
などのプリミティブデータ型用の最適化されたインタフェースもあります。
注:典型的なJavaランタイムは、 valueOf
ファクトリメソッドおよびオートボクシングによって使用されるInteger
およびその他のプリミティブラッパーオブジェクトの単純なキャッシュを保持します。 Integer
の場合、このキャッシュのデフォルトの範囲は-128〜+127です。 JVMの中には、キャッシュサイズ/範囲を変更するためのJVMコマンドラインオプションがあります。
異なるケースIntegerとintを区別なく使用できる場合
ケース1:メソッドの引数の代わりに使用しています。
メソッドが引数としてラッパークラスのオブジェクトを必要とする場合、引数はそれぞれのプリミティブ型の変数にやりとりすることができます。
例:
int i;
Integer j;
void ex_method(Integer i)//Is a valid statement
void ex_method1(int j)//Is a valid statement
ケース2:戻り値を渡しているとき:
メソッドがプリミティブ型変数を返すとき、対応するラッパークラスのオブジェクトは戻り値として同じ意味で渡され、逆も同様です。
例:
int i;
Integer j;
int ex_method()
{...
return j;}//Is a valid statement
Integer ex_method1()
{...
return i;//Is a valid statement
}
ケース3:操作を実行中。
数値に対して演算を行うときはいつでも、それぞれのラッパークラスのプリミティブ型変数とオブジェクトは同じ意味で使用できます。
int i=5;
Integer j=new Integer(7);
int k=i+j;//Is a valid statement
Integer m=i+j;//Is also a valid statement
落とし穴 :ラッパークラスのオブジェクトに値を初期化または割り当てることを忘れないでください。
ラッパークラスオブジェクトとプリミティブ変数を同じ意味で使用している間に、ラッパークラスオブジェクトに値を初期化したり割り当てたりすることを忘れることはありません。実行時にnullポインター例外が発生する可能性があります。
例:
public class Test{
Integer i;
int j;
public void met()
{j=i;//Null pointer exception
SOP(j);
SOP(i);}
public static void main(String[] args)
{Test t=new Test();
t.go();//Null pointer exception
}
上記の例では、オブジェクトの値は割り当てられていないため、初期化されていないため、実行時にプログラムはNULLポインタ例外になります。上記の例から明らかなように、objectの値は決して初期化されず、割り当てられません。