web-dev-qa-db-ja.com

Javaの奇妙な整数ボクシング

私はこれに似たコードを見ました:

public class Scratch
{
    public static void main(String[] args)
    {
        Integer a = 1000, b = 1000;
        System.out.println(a == b);

        Integer c = 100, d = 100;
        System.out.println(c == d);
    }
}

実行すると、次のコードブロックが出力されます。

false
true

最初のオブジェクトがfalse:である理由を理解します。2つのオブジェクトが別々のオブジェクトなので、==は参照を比較します。しかし、私は理解できません、なぜ2番目のステートメントがtrueを返すのですか?整数の値が特定の範囲内にあるときに起動する奇妙なオートボクシングルールはありますか?何が起きてる?

105
Joel

true行は、実際に言語仕様によって保証されています。 セクション5.1.7 から:

ボックス化される値pがtrue、false、バイト、\ u0000〜\ u007fの範囲のchar、または-128〜127のintまたはshort数値の場合、r1とr2を2つのボクシング変換の結果とします。 pの。常にr1 == r2です。

議論は続き、出力の2行目は保証されていますが、最初の行は保証されていないことを示唆しています(以下に引用する最後の段落を参照)。

理想的には、特定のプリミティブ値pをボックス化すると、常に同一の参照が生成されます。実際には、これは既存の実装技術を使用して実行できない場合があります。上記のルールは実用的な妥協案です。上記の最後の節では、特定の共通値を常に区別できないオブジェクトにボックス化する必要があります。実装は、これらを遅延的にまたは熱心にキャッシュします。

他の値については、この定式化により、プログラマー側のボックス化された値の同一性に関する仮定が許可されません。これにより、これらの参照の一部またはすべてを共有できます(必須ではありません)。

これにより、ほとんどの一般的な場合、特に小さなデバイスで過度のパフォーマンスペナルティを課すことなく、動作が目的の動作になります。たとえば、メモリ制限の少ない実装では、すべての文字とショート、および-32K〜+ 32Kの範囲の整数とロングをキャッシュできます。

96
Jon Skeet
public class Scratch
{
   public static void main(String[] args)
    {
        Integer a = 1000, b = 1000;  //1
        System.out.println(a == b);

        Integer c = 100, d = 100;  //2
        System.out.println(c == d);
   }
}

出力:

false
true

はい、参照を比較するために最初の出力が生成されます。 「a」と「b」-これらは2つの異なる参照です。ポイント1では、実際には次のような2つの参照が作成されます-

Integer a = new Integer(1000);
Integer b = new Integer(1000);

2番目の出力が生成されるのは、JVMが範囲(-128から127)に入ると、Integerがメモリを節約しようとするためです。ポイント2では、 'd'に対してInteger型の新しい参照は作成されません。整数型参照変数「d」の新しいオブジェクトを作成する代わりに、「c」で参照される以前に作成されたオブジェクトにのみ割り当てます。これらはすべてJVMによって行われます。

これらのメモリ節約ルールは、整数専用ではありません。メモリを節約するために、次のラッパーオブジェクトの2つのインスタンス(ボクシングによって作成されている間)は、常に==になります。ここで、プリミティブ値は同じです-

  • ブール値
  • バイト
  • \ u00から\u007f(7fは10進数で127)の文字
  • -128から127までの短い整数
25
Razib

ある範囲(おそらく-128〜127)の整数オブジェクトはキャッシュされ、再利用されます。その範囲外の整数は、毎回新しいオブジェクトを取得します。

8
Adam Crume

それは興味深い点です。本の中で Effective Java は、自分のクラスのequalsを常にオーバーライドすることを提案しています。また、Javaクラスの2つのオブジェクトインスタンスの等価性をチェックするには、常にequalsメソッドを使用します。

public class Scratch
{
    public static void main(String[] args)
    {
        Integer a = 1000, b = 1000;
        System.out.println(a.equals(b));

        Integer c = 100, d = 100;
        System.out.println(c.equals(d));
    }
}

戻り値:

true
true
4
AmirHd

はい、値が特定の範囲内にあるときに起動する奇妙なオートボクシングルールがあります。 Object変数に定数を割り当てると、言語定義に新しいオブジェクトmustが作成されることを示すものはありません。キャッシュから既存のオブジェクトを再利用できます。

実際、JVMは通常、この目的のために小さな整数のキャッシュと、Boolean.TRUEやBoolean.FALSEなどの値を保存します。

4
Avi

Java整数の場合、ボクシングは-128〜127の範囲で機能します。この範囲の数値を使用する場合、==演算子と比較できます。範囲外の整数オブジェクトの場合等しいを使用する必要があります。

3
marvin

私の推測では、Javaは非常に一般的であり、既存のオブジェクトを再利用するためにかなりの時間を節約するため、すでに「ボックス化」されている小さな整数のキャッシュを保持します新しいものを作成します。

3
Omnifarious

整数参照へのintリテラルの直接割り当ては、オブジェクト変換コードへのリテラル値がコンパイラーによって処理されるオートボクシングの例です。

そのため、コンパイル段階でコンパイラは_Integer a = 1000, b = 1000;_をInteger a = Integer.valueOf(1000), b = Integer.valueOf(1000);に変換します。

したがって、実際に整数オブジェクトを提供するのはInteger.valueOf()メソッドであり、Integer.valueOf()メソッドのソースコードを見ると、メソッドが-128の範囲の整数オブジェクトをキャッシュしていることが明確にわかります。 127まで(包括的)。

_/**
 *
 * This method will always cache values in the range -128 to 127,
 * inclusive, and may cache other values outside of this range.
 *
 * @param  i an {@code int} value.
 * @return an {@code Integer} instance representing {@code i}.
 * @since  1.5
 */
 public static Integer valueOf(int i) {
     if (i >= IntegerCache.low && i <= IntegerCache.high)
         return IntegerCache.cache[i + (-IntegerCache.low)];
     return new Integer(i);
 }
_

したがって、新しい整数オブジェクトを作成して返す代わりに、Integer.valueOf()メソッドは、渡されたintリテラルが-128より大きく127より小さい場合、内部IntegerCacheから整数オブジェクトを返します。

Javaはこれらの整数オブジェクトをキャッシュします。これは、この範囲の整数が日々のプログラミングで頻繁に使用され、間接的にメモリを節約するためです。

キャッシュは、静的ブロックのためにクラスがメモリにロードされる最初の使用時に初期化されます。キャッシュの最大範囲は、_-XX:AutoBoxCacheMax_ JVMオプションによって制御できます。

このキャッシュ動作は、Integerオブジェクトのみには適用されません。Integer.IntegerCacheと同様に、それぞれ_ByteCache, ShortCache, LongCache, CharacterCache_には_Byte, Short, Long, Character_もあります。

詳細については、私の記事をご覧ください Java整数キャッシュ-Integer.valueOf(127 Integer.valueOf(127)がTrueである理由)==。

1
Naresh Joshi

Java 5では、メモリを節約し、整数型オブジェクトの処理のパフォーマンスを向上させる新しい機能が導入されました。整数オブジェクトは内部でキャッシュされ、同じ参照オブジェクトを介して再利用されます。

  1. これは、–127〜+127(最大整数値)の範囲の整数値に適用されます。

  2. この整数キャッシュは、オートボクシングでのみ機能します。整数オブジェクトは、コンストラクターを使用して構築されるときにキャッシュされません。

詳細については、以下のリンクをご覧ください:

整数キャッシュの詳細

0
Rahul Maurya

Integer対象のソースコードを確認すると、次のようなvalueOfメソッドのソースが見つかります。

public static Integer valueOf(int i) {
    if (i >= IntegerCache.low && i <= IntegerCache.high)
        return IntegerCache.cache[i + (-IntegerCache.low)];
    return new Integer(i);
}

Integerオブジェクトが-128(Integer.low)〜127(Integer.high)は、オートボクシング中に同じ参照オブジェクトです。また、IntegerCacheクラスのプライベート静的内部クラスであるIntegerキャッシュ配列を処理するクラスIntegerがあることがわかります。

この奇妙な状況を理解するのに役立つ別の興味深い例があります。

public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {

      Class cache = Integer.class.getDeclaredClasses()[0]; 
      Field myCache = cache.getDeclaredField("cache"); 
      myCache.setAccessible(true);

      Integer[] newCache = (Integer[]) myCache.get(cache); 
      newCache[132] = newCache[133]; 

      Integer a = 2;
      Integer b = a + a;
      System.out.printf("%d + %d = %d", a, a, b); //The output is: 2 + 2 = 5    

}
0
L Joey