SCJP Java 6 by Kathy SierraとBert Bates)を読んでいて、この本は私をとても混乱させています。245ページで、彼らは以下のコードを述べています。
Integer i1 = 1000;
Integer i2 = 1000;
if(i1 != i2)
System.out.println("different objects");
//Prints output
different objects
次に、次のページに次のコードがあります
Integer i3 = 10;
Integer i4 = 10;
if(i3 == i4)
System.out.println("same objects");
//Prints output
same objects
私は困惑している!これを自分で試してみると、==を使用してequals()メソッドを使用するのと同じ方法で比較することはできないようです。 ==を使用すると、整数変数が同じ値(つまり10)に設定されている場合でも、常に「false」が返されます。私は正しいですか? ==を使用して同じ整数オブジェクトを(同じ値で)比較すると、常に「false」になります。
答えの鍵はobject interningと呼ばれます。 Javaインターンの小さな数値(128未満)であるため、インターンされた範囲内のn
を含むInteger(n)
のすべてのインスタンスは同じです。数値以上〜128はインターンされないため、Integer(1000)
オブジェクトは互いに等しくありません。
Integer
のソースコードを見ると、Integer.valueOf(int)
poolsすべての値-128〜127であることがわかります。理由は、小さな整数値が使用されているためです。頻繁に行われるため、プール/キャッシュする価値があります。
Integer.Java
:
public static Integer valueOf(int i) {
if(i >= -128 && i <= IntegerCache.high)
return IntegerCache.cache[i + 128];
else
return new Integer(i);
}
このプーリングは実装固有であり、プールされた範囲の保証はありません。
インターンに関する答えは概念的には正しいですが、用語では正しくありません。 Javaでのインターンは、通常、Javaランタイムがプーリング(Stringのインターンなど)を実行していることを意味します。整数の場合、プーリングを行っているのはクラス自体です。 。関係するJVMマジックはありません。
上記のインターニングに関する答えは正解です。ただし、次のことを検討する必要があります。
Integer i3 = new Integer(10);
Integer i4 = new Integer(10);
新しいオブジェクトを明示的に作成したため、新しいオブジェクトはありません。次のようにコードを記述すると、インターンされます。
Integer i3 = Integer.valueOf(10);
Integer i4 = Integer.valueOf(10);
これらは再び同じオブジェクトになります。 src.ZipファイルのInteger.Javaクラス内のvalueOfメソッドを見ると、intの値が-128から127の範囲外であるかどうかを確認する場所を確認できます。それ以外の場合は、新しいIntegerクラスを呼び出します。キャッシュからロードします。
_Integer i1 = 1000;
Integer i2 = 1000;
_
コンパイラーは、int 1000をIntegerオブジェクトとして「ボックス化」します。これを行うには、ソースを次のように変換します。
_Integer i1 = Integer.valueOf(1000);
Integer i2 = Integer.valueOf(1000);
_
ここでvalueOf
はnew Integer(1000)
への単純な呼び出しになる可能性がありますが、int
がボックス化されるたびに新しいIntegerオブジェクトを作成すると、時間とスペースの両方が消費されます。これを回避するために、Integerクラスは、限られた範囲のint値に対してIntegerオブジェクトの配列を保持します。
_if(value> maxRange || value< minRange){
//not in pool return new Integer
return new Integer(value);
}else{
//return pooled Integer object
//for the value, pool contains all Integer
//values from minRange to maxRange
return integerPool[value-minRange];
}
_
プログラムの開始時にjvm引数を使用して範囲を設定することで、獲得速度とこれに失われるメモリを調整できます(デフォルトでは-127〜128です)。
jls-5.1.7 によると
If the value p being boxed is true, false, a byte, or a char in the range \u0000 to \u007f,
or an int or short number between -128 and 127 (inclusive), then let r1 and r2
be the results of any two boxing conversions of p. It is always the case that r1 == r2.
したがって、-128と127の間の数値は、Intergerクラスによってキャッシュされます。
2つのオブジェクトを比較するときは常にequals
メソッドを使用することを忘れないでください。
キャッシングコードは、IntegerCache
クラスのメンバーであるInteger
クラスで記述されています。
コードスニペットは次のとおりです。
/**
* Cache to support the object identity semantics of autoboxing for values between
* -128 and 127 (inclusive) as required by JLS.
*
* The cache is initialized on first usage. The size of the cache
* may be controlled by the {@code -XX:AutoBoxCacheMax=<size>} option.
* During VM initialization, Java.lang.Integer.IntegerCache.high property
* may be set and saved in the private system properties in the
* Sun.misc.VM class.
*/
private static class IntegerCache {
static final int low = -128;
static final int high;
static final Integer cache[];
static {
// high value may be configured by property
int h = 127;
String integerCacheHighPropValue =
Sun.misc.VM.getSavedProperty("Java.lang.Integer.IntegerCache.high");
if (integerCacheHighPropValue != null) {
try {
int i = parseInt(integerCacheHighPropValue);
i = Math.max(i, 127);
// Maximum array size is Integer.MAX_VALUE
h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
} catch( NumberFormatException nfe) {
// If the property cannot be parsed into an int, ignore it.
}
}
high = h;
cache = new Integer[(high - low) + 1];
int j = low;
for(int k = 0; k < cache.length; k++)
cache[k] = new Integer(j++);
// range [-128, 127] must be interned (JLS7 5.1.7)
assert IntegerCache.high >= 127;
}
private IntegerCache() {}
}
==と!=を使用した文字列比較と整数比較では、期待どおりのブール値が得られません。
Java ==演算子を使用してプリミティブ型以外のものを比較する場合、参照の等価性をチェックします。これは、比較されるものがラップされたプリミティブである場合にも当てはまります。さらに、valueOf
メソッドとコンパイラによって生成されたオートボクシングステートメントは、通常、他の既存の参照と参照が等しくない新しいオブジェクトを任意に返すか、既存のオブジェクトへの参照を返します(もちろん、参照は同じオブジェクトを識別する既存の参照と同じです。実装は、Integer
インスタンスの「プール」を値-128〜127に維持する必要があります。これにより、_Integer.valueOf
_へのすべての呼び出しがその範囲内の特定の数値は同じオブジェクトへの参照を返しますが、それ以外の実装は次のようなことを自由に行うことができます
_static Integer [] intPool = new Integer[256];
public Integer valueOf(int n)
{
int hash = (n*0x18675309) >>> 24;
Integer instance = intPool[n];
if (instance == null && instance.value != n)
{
instance = new Integer(n);
intPool[hash] = instance ;
}
return instance;
}
_
私は特にJava実装がそのようなことをすることを期待していません。多くの場合、「キャッシュヒット」率は0%に近く、キャッシュ内のインスタンスを探すために費やされる余分な時間はそれでも、instanceOf
によって返される参照が、そのメソッドによって返される以前の参照と一致しないという保証はありません(たとえlast参照と一致しなくても)そのメソッドによって返された場合、一部のキャッシングアルゴリズムは、特にプールがロックされていない複数のスレッドによって共有されている場合、earlier参照を返す可能性があります。ロックがなくても、コードが他のコードを返すことはありません正しい値の整数への参照よりも、しかし返された参照が等しいと比較する予測できない変動を引き起こす可能性があります。コンストラクタnew Integer(n)
を使用して直接作成されたInteger
オブジェクトへの参照のみが保証されます一意; valueOf
によって返される参照がどの参照とも一致しないことを期待するコードvalueOf
から返されたrenceは、実際には一致しないことに気づかなかったため、壊れていると見なされます。
"=="は常に値のメモリロケーションまたはオブジェクト参照を比較します。 equalsメソッドは常に値を比較しますが、equalsは間接的に "=="演算子を使用して値を比較します。 Integerは、整数キャッシュを使用して-128から+127までの値を格納します。==演算子を使用して-128から127までの値をチェックすると、trueが返されます。 -128から127までのいずれかの値として
Integer i1 = -128;
Integer i2 = -128;
System.out.println(i1 == i2); // returns true
上記の範囲以外の場合はfalseを返します
Integer i1 = 1000;
Integer i2 = 1000;
System.out.println(i1 == i2); // returns false
リンクを参照 いくつかの追加情報