Использование оператора == в Java для сравнения объектов-оболочек



Я читаю SCJP Java 6 Кэти Сиерры и Берта Бейтса, и эта книга меня очень смущает. На странице 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 (). Использование == всегда дает мне 'false', даже если целочисленные переменные имеют одинаковое значение (например, 10). Я правильно? Использование == для сравнения одного и того же целого объекта (с одинаковыми значениями) всегда приводит к 'false'

769   8  

8 ответов:

Ключ к ответу называетсяинтернирование объекта . Java интернирует небольшие числа (меньше 128), поэтому все экземпляры Integer(n) с n в интернированном диапазоне одинаковы. Числа, большие или равные 128, не интернируются, следовательно, объекты Integer(1000) не равны друг другу.

Если вы посмотрите на исходный код для Integer ,вы увидите, что Integer.valueOf(int) пулы все значения от -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 выполняет объединение (например, стажер строки). В случае Integer это сам класс, который делает объединение. Здесь нет никакой магии СПМ.

Приведенный выше ответ об интернировании верен. Что-то, чтобы рассмотреть, хотя, если вы делаете:

Integer i3 = new Integer(10);
Integer i4 = new Integer(10);

У вас не будет новых объектов, так как вы создали новые объекты явно. Если вы напишете код следующим образом, он будет интернирован:

Integer i3 = Integer.valueOf(10);
Integer i4 = Integer.valueOf(10);
Теперь они снова будут одним и тем же объектом. Если вы посмотрите на метод valueOf внутри целого числа.класс java в src.zip-файл вы можете увидеть, где он проверяет, находится ли значение int за пределами от -128 до 127 он вызывает новый целочисленный класс иначе он загружает его из кэша.
Integer i1 = 1000;
Integer i2 = 1000;

Компилятор "упаковывает" int 1000 как целочисленный объект. Для этого он преобразует источник в следующий:

Integer i1 = Integer.valueOf(1000);
Integer i2 = Integer.valueOf(1000);

Теперь valueOf может быть простым вызовом new Integer(1000), однако создание нового целочисленного объекта каждый раз, когда int помещается в коробку, будет стоить как времени, так и пространства. Чтобы избежать этого, класс Integer сохраняет массив целочисленных объектов для ограниченного диапазона значений int.

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 аргумент при запуске программы (afaik по умолчанию от -127 до 128).

Когда оператор Java == используется для сравнения чего-либо, кроме примитивных типов, он проверяет ссылочное равенство; это применимо даже тогда, когда сравниваемые объекты являются обернутыми примитивами. Кроме того, метод valueOf и сгенерированный компилятором оператор autoboxing обычно свободны произвольно возвращать новый объект, который не будет ссылочным-равным любой другой ранее существующей ссылке, или возвращать ссылку на существующий объект (который, конечно же, будет ссылочным-равным любому другому объекту). ранее существовавшая ссылка, идентифицирующая тот же объект). Реализации необходимы для поддержания "пула" экземпляров 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, не будет соответствовать некоторой предыдущей ссылке, возвращенной этим методом (даже если она не соответствует последней ссылке , возвращенной этим методом, некоторые алгоритмы кэширования могут привести к возвращению более ранней ссылки, особенно если пул совместно используется несколькими потоками без блокировки. Отсутствие блокировки никогда не вызовет код возвращать что-либо, кроме ссылки на целое число с правильным значением, но может вызвать непредсказуемые вариации, в которых возвращаемые ссылки сравниваются равными). Только ссылки на объекты Integer, созданные непосредственно с помощью конструктора new Integer(n), гарантированно уникальны; код, который ожидает, что любая ссылка, возвращенная valueOf, не будет соответствовать любой ссылке, возвращенной valueOf, не наблюдая на самом деле, что она не соответствует, должен считаться сломанным.

Сравнение строк и целых чисел с помощью = = и != дает булевы результаты не так, как мы expect.So будьте осторожны и убедитесь , что возможные неизвестные результаты не препятствуют производительности, надежности и точности вашего программного обеспечения.

"== " Всегда сравнивайте расположение в памяти или ссылки на объекты значений. метод 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

Обратитесь по ссылке для получения дополнительной информации

Согласно 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.
Помните, что при сравнении двух объектов всегда используйте метод 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() {}
}

Ссылки

Comments

    Ничего не найдено.