NHN Cloud NHN Cloud Meetup!

[Java] Integer.valueOf(127)== Integer.valueOf(127)は真か?

Javaには最適化のためオブジェクトをキャッシュするロジックがあります。キャッシュロジックは、アプリケーションのパフォーマンス改善に役立ちますが、意図しない結果を発生させることがあります。さらにクリティカルな障害状況の原因にもなります。

Naresh Joshiの[Java Integer Cache – Why Integer.valueOf(127) == Integer.valueOf(127) Is True]のブログ記事内容を紹介します。Java Integer Cacheについて考えてみましょう。


インタビューで、次のような質問を受けました。

Integer a = 127; 
Integer b = 127;

上のような2つのIntegerオブジェクトがあります。

a == b (true? false?)

a == bの答えは何でしょうか?

ここで、この質問に対する答えと、その説明を述べたいと思います。
簡単に言うとこうなります。auto-boxingの一例として、intの数値リテラルを参照型であるIntegerに直接接代入できます。auto-boxingでは、コンパイラがリテラル値をオブジェクトに変換するコードを実行します。コンパイルの段階で、コンパイラはInteger a = 127;を、Integer a = Integer.valueOf(127);に変更します。

Integerクラスは、内部にIntegerCacheを持っていて、デフォルトでは -128 ~ 127の整数値をキャッシュします。Integer.valueOf()メソッドは、キャッシュの範囲に該当するオブジェクトを返します。abは、同じオブジェクトを指すので、a == btrueになります。


もう少し簡単に理解できるように、2つのカテゴリーに分類されるJavaタイプを見てみましょう。

  1. プリミティブ型(Primitive Types):Javaには、8つのプリミティブ型(byte, short, int, long, float, double, char, boolean)があり、バイナリ値を直接持っています。
  2. 参照型(Reference Types):プリミティブ型ではないものはすべては参照型です。たとえば、Classes, Interfaces, Enums, Arraysなどです。参照型は、「オブジェクト自身」を持つのではなく、「object address」を持っています。
    たとえば、Integer a = new Integer(5); Integer b = new Integer(5);のとき、aとbはバイナリ値5を持つのではなく、それぞれ5の値を持つオブジェクトのメモリアドレスの値を持っています。
    もしa == bを用いてabを比較すると、実際には2つの別々のメモリアドレスを比較することになり、falseを取得するため、abの同値比較(equalityの検証)をするときは、a.equals(b)を実施する必要があります。

Javaは、すべてのプリミティブ型に対してwrapper classesを提供しており、auto-boxing, auto-unboxingをサポートします。

// auto-boxingの例、cは参照型
Integer c = 128; // コンパイラが次のように変換します : Integer c = Integer.valueOf(128);

// auto-unboxingの例、aはプリミティブ型 
int e = c; //  コンパイラが次のように変換します : int e = c.intValue();

もし、abのintergerオブジェクトを作成し、比較演算子==でそれらを比較すると、false値を取得できます。2つのリファレンスは、それぞれ異なるオブジェクトを持っているからです。

Integer a = 128; // コンパイラが次のように変換します : Integer a = Integer.valueOf(128);
Integer b = 128; // コンパイラが次のように変換します : Integer b = Integer.valueOf(128);

System.out.println(a == b); // false

 


しかし、このように127という値を代入して、==で比較すると、true値を取得します。なぜでしょうか?

Integer a = 127; // コンパイラが次のように変換します : Integer a = Integer.valueOf(127);

Integer b = 127; // コンパイラが次のように変換します : Integer b = Integer.valueOf(127);

System.out.println(a == b); // true

上記のコードから、abに異なるオブジェクトを代入したにも関わらず、a == btrueを返すということは、abが同じオブジェクトを指していることを意味します。

どのようにして、この比較文がtrueを返すのでしょうか?abが同じオブジェクトを指しているのでしょうか?
Integer a =127;構文がauto-boxingの例であり、コンパイラが自動的にこのラインをInteger a = Integer.valueOf(127);に変換します。

このようなinteger objectを返してくるInteger.valueOf()メソッドが、何かをしているようです。
Integer.valueOf()のソースコードを見ると、次のようなことが分かります。

IntegerCache.lowより大きく、IntegerCache.highより小さいintリテラルiを超えた場合、Integer.valueOf()メソッドは、IntegerCacheからIntegerオブジェクトを返します。
IntegerCache.lowIntegerCache.highのデフォルト値は、それぞれ-128127です。

つまり、Integer.valueOf()メソッドは、-128から127の間のint リテラル値を伝達する際、新しいIntegerオブジェクトを生成して応答する代わりに、内部のIntegerCacheオブジェクトでIntegerオブジェクトを返しているということです。

/** 
* 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) { 
    if (i >= IntegerCache.low && i <= IntegerCache.high) 
        return IntegerCache.cache[i + (-IntegerCache.low)]; 
    return new Integer(i); 
}

Javaは-128から127の範囲の整数オブジェクトをキャッシュします。
なぜなら、この範囲の整数は、開発時に頻繁に使用されるからです。

キャッシュは、static 初期化ブロックによってIntegerCacheクラスがメモリにロードされる初回に初期化されます。
キャッシュの最大値は、JVMオプションを使用して、次のように調整できます。

-XX:AutoBoxCacheMax

これらのキャッシュは、Integerオブジェクトにのみ適用されるものではありません。
Integer.IntegerCacheのように、ByteCacheShortCacheLongCacheCharacterCacheもそれぞれ存在します。

Byte, Short, Longタイプは-127から127までの(-127<=, <=127)固定されたキャッシュ値を持ちます。
しかし、Characterは0から127(0<=, <=127)までの固定されたキャッシュ値を持ちます。
範囲修正はIntegerのみ変更が可能で、他のタイプには変更できません。

参考までに、ここで扱った完全なソースコードは下記から参照いただけます。
Github Repository

NHN Cloud Meetup 編集部

NHN Cloudの技術ナレッジやお得なイベント情報を発信していきます
pagetop