Javaで整数ラッパーを比較するときに、128 == 128がfalseであるのに、127 == 127がtrueであるのはなぜですか?


172
class D {
    public static void main(String args[]) {
        Integer b2=128;
        Integer b3=128;
        System.out.println(b2==b3);
    }
}

出力:

false

class D {
    public static void main(String args[]) {
        Integer b2=127;
        Integer b3=127;
        System.out.println(b2==b3);
    }
}

出力:

true

注:-128から127までの数値が当てはまります。



1
どのようにしてこの質問をするようになりましたか?それは本当に楽しいですが、「現実の世界」のようなものに出くわすことはありません...または?
Mare Infinitus 2012年

回答:


217

Javaで数値リテラルをコンパイルし、それをInteger(大文字I)に割り当てると、コンパイラーは以下を発行します。

Integer b2 =Integer.valueOf(127)

このコード行は、オートボクシングを使用したときにも生成されます。

valueOf 特定の数値が「プール」されるように実装され、128より小さい値に対して同じインスタンスを返します。

Java 1.6ソースコードの621行目:

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

の値はhigh、システムプロパティを使用して別の値に設定できます。

-Djava.lang.Integer.IntegerCache.high = 999

そのシステムプロパティでプログラムを実行すると、trueが出力されます。

明白な結論:2つの参照が同一であることに決して依存せず、常にそれらを.equals()メソッドと比較してください。

したがってb2.equals(b3)、b2、b3のすべての論理的に等しい値に対してtrueを出力します。

Integerキャッシュはパフォーマンス上の理由からではなく、JLSのセクション5.1.7に準拠していることに注意してください。オブジェクトIDは、-128から127までの値に指定する必要があります。

Integer#valueOf(int)もこの動作を文書化します:

この方法では、頻繁に要求される値をキャッシュすることにより、スペースと時間のパフォーマンスが大幅に向上する可能性があります。このメソッドは常に-128から127までの範囲の値をキャッシュし、この範囲外の他の値をキャッシュする場合があります。


1
127より小さい値はJavaによって無視され、Integer.MAX_VALUE-128より大きい値は制限されることに注意してください。
Andreas Petersson

Java 5以降では、整数がバイト値用にキャッシュされ、new Integer(1)== new Integer(1)になります。ただし、これはJava 1.4以下の場合には当てはまらないため、最終的にその環境にダウングレードする必要がある場合は注意してください。
MetroidFan2002 2009年

11
いいえ、これは間違っています。new Integer(1)== new Integer(1)は、jvmに関係なくfalseです。私の知る限り、コンパイラは "new"キーワードでチートしません。常に新しいオブジェクトをインスタンス化する必要があります。
Andreas Petersson、

1
@ホルガー興味深い点。しかし、JDKのIntegerクラスをカスタム実装に置き換えることは技術的に可能です...(なぜ誰かがその気が狂っているのかと尋ねないでください)-最適化が許可されない副作用がある可能性があります
Andreas Petersson

1
@AndreasPetersson確かに。「コンパイラ」とは、JITコンパイラを意味します。JITコンパイラは、実際の実装クラスを正確に認識しており、コンストラクタに副作用がない場合にのみ最適化できます。または、式を最適化して副作用のみを再現し、続いてを使用しfalseます。実際、これは、エスケープ分析とスカラー置換を適用することの副作用として、今日すでに起こっている可能性があります。
Holger、

24

オートボクシングキャッシュ-128〜127。これは、JLS(5.1.7)で指定されています。

ボックス化される p がtrue、false、バイト、\ u0000から\ u007fの範囲の文字、または-128から127までの整数または短い数値の場合、r1とr2を2つのボックス化変換の結果とするの 常にr1 == r2です。

オブジェクトを処理するときに覚えておくべき簡単なルールは.equals、2つのオブジェクトが「等しい」かどうかを確認する場合に使用==し、同じインスタンスを指しているかどうかを確認する場合に使用します。


1
注:JLSはJava 9で変更されました。これは現在、コンパイル時の定数式でのみ保証されています。承認された回答の更新を参照してください。
スティーブンC

9

プリミティブデータ型のintを使用すると、どちらの場合もtrueになり、期待される出力が得られます。

ただし、Integerオブジェクトを使用しているため、==演算子の意味は異なります。

オブジェクトのコンテキストでは、==は、変数が同じオブジェクト参照を参照しているかどうかを確認します。

オブジェクトの値を比較するには、equals()メソッドを使用する必要があります。

 b2.equals(b1)

b2がb1より小さいか、大きいか等しいかを示します(詳細については、APIを確認してください)


7

Java関連のメモリ最適化です。

メモリを節約するために、Javaは値が次の範囲にあるすべてのラッパーオブジェクトを「再利用」します。

すべてのブール値(trueおよびfalse)

すべてのバイト値

\ u0000から\ u007fまでのすべての文字値(つまり、10進数では0から127)

-128〜127のすべてのShortおよびInteger値。


3

Integer.javaを見てください。値が-128から127の間の場合、キャッシュされたプールを使用するため(Integer) 1 == (Integer) 1(Integer) 222 != (Integer) 222

 /**
 * Returns an {@code Integer} instance representing the specified
 * {@code int} value.  If a new {@code Integer} instance is not
 * required, this method should generally be used in preference to
 * the constructor {@link #Integer(int)}, as this method is likely
 * to yield significantly better space and time performance by
 * caching frequently requested values.
 *
 * 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) {
    assert IntegerCache.high >= 127;
    if (i >= IntegerCache.low && i <= IntegerCache.high)
        return IntegerCache.cache[i + (-IntegerCache.low)];
    return new Integer(i);
}       

0

他の回答は、観察された効果を観察できる理由を説明していますが、それはプログラマーにとって非常に重要な点です(興味深いですが、実際のコードを書くときに忘れてはならないことです)。

Integerオブジェクトが等しいかどうかを比較するには、equalsメソッドを使用します。

識別演算子を使用して、整数オブジェクトの等価性を比較しないでください==

いくつかの等しい値が同一のオブジェクトである場合がありますが、これは一般に依存する必要があるものではありません。


-4

この問題はIntegerに固有のものではないため、次のように書きました。私の結論は、APIを誤って使用すると、多くの場合、正しくない動作が見られるということです。正しく使用すると、正しい動作が表示されます。

public static void main (String[] args) {
    Byte b1=127;
    Byte b2=127;

    Short s1=127; //incorrect should use Byte
    Short s2=127; //incorrect should use Byte
    Short s3=128;
    Short s4=128;

    Integer i1=127; //incorrect should use Byte
    Integer i2=127; //incorrect should use Byte
    Integer i3=128;
    Integer i4=128;

    Integer i5=32767; //incorrect should use Short
    Integer i6=32767; //incorrect should use Short

    Long l1=127L;           //incorrect should use Byte
    Long l2=127L;           //incorrect should use Byte
    Long l3=13267L;         //incorrect should use Short
    Long l4=32767L;         //incorrect should use Short
    Long l5=2147483647L;    //incorrect should use Integer 
    Long l6=2147483647L;    //incorrect should use Integer
    Long l7=2147483648L;
    Long l8=2147483648L;

    System.out.print(b1==b2); //true  (incorrect) Used API correctly
    System.out.print(s1==s2); //true  (incorrect) Used API incorrectly
    System.out.print(i1==i2); //true  (incorrect) Used API incorrectly
    System.out.print(l1==l2); //true  (incorrect) Used API incorrectly

    System.out.print(s3==s4); //false (correct) Used API correctly
    System.out.print(i3==i4); //false (correct) Used API correctly
    System.out.print(i5==i6); //false (correct) Used API correctly
    System.out.print(l3==l4); //false (correct) Used API correctly
    System.out.print(l7==l8); //false (correct) Used API correctly
    System.out.print(l5==l6); //false (correct) Used API incorrectly

}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.