Почему сравнения == с Integer.valueOf (String) дают разные результаты для 127 и 128?

182

Я понятия не имею, почему эти строки кода возвращают разные значения:

System.out.println(Integer.valueOf("127")==Integer.valueOf("127"));
System.out.println(Integer.valueOf("128")==Integer.valueOf("128"));
System.out.println(Integer.parseInt("128")==Integer.valueOf("128"));

Выход:

true
false
true

Почему первый возвращается, trueа второй возвращается false? Есть ли что-то другое, что я не знаю между 127и 128? (Конечно, я знаю, что 127< 128.)

Кроме того, почему третий возвращается true?

Я прочитал ответ на этот вопрос , но до сих пор не понял, как он может вернуться trueи почему возвращается код во второй строке false.

DNR
источник
6
Целое число - это объект; если вы хотите сравнить на равенство, используйте .equals(), иначе все ставки выключены.
Карл Дамгаард Асмуссен
6
@KarlDamgaardAsmussen На самом деле здесь я действительно хочу проверить, являются ли они ссылками на один и тот же объект, и сначала я не понимаю, почему 127 128 возвращают другой результат.
ДНР
@DnR, если бы Java был языком со стандартизированной спецификацией, я бы подумал, что это позволит решить такие вопросы до реализации или даже предписанного неопределенного поведения.
Карл Дамгаард Асмуссен
1
@jszumski: Там больше этот вопрос , чем просто часть кэширования, хотя. Кроме того, связанный ответ является в лучшем случае неполным - он не совсем подробно описывает, что кешируется и почему.
Макото
1
Для дальнейшего изучения этого обсуждения, пожалуйста, обратитесь к этой мета-публикации .
Йерун Ванневел

Ответы:

191

Здесь есть разительная разница.

valueOfвозвращает Integerобъект, значения которого могут быть кэшированы в диапазоне от -128 до 127. Вот почему первое значение возвращается true- оно кэшируется, а второе возвращается false- 128 не является кэшированным значением, поэтому вы получаете два отдельных Integerэкземпляра ,

Важно отметить, что вы сравниваете ссылки Integer#valueOf, и если вы сравниваете значение, которое больше, чем поддерживает кэш, оно не будет оцениваться true, даже если проанализированные значения эквивалентны (в данном случае:) Integer.valueOf(128) == Integer.valueOf(128). Вы должны использовать equals()вместо этого.

parseIntвозвращается примитив int. Поэтому возвращается третье значение true- 128 == 128оценивается, и, конечно же , true.

Теперь случается так, что получается третий результат true:

  • Преобразование распаковки происходит в отношении оператора эквивалентности, который вы используете, и ваших типов данных, а именно, intи Integer. Вы получаете Integerс valueOfправой стороны, конечно.

  • После преобразования вы сравниваете два примитивных intзначения. Сравнение происходит так же, как вы ожидаете, что касается примитивов, поэтому вы сравниваете 128и 128.

Makoto
источник
2
@ user3152527: Есть существенная разница - один считается объектом, что означает, что вы можете вызывать методы и взаимодействовать с ним в абстрактных структурах данных, например List. Другой - примитив, который является просто необработанным значением.
Макото
1
@ user3152527 Вы задали отличный вопрос (в худшем случае не тупой). Но вы исправили, чтобы использовать .equals, верно?
user2910265
3
Ах, похоже, что спрашивающий не понял основополагающего факта в Java: при использовании «==» для сравнения двух объектов вы проверяете, являются ли они ссылками на один и тот же объект. При использовании «equals ()» вы проверяете, имеют ли они одинаковое значение. Вы не можете использовать «равно» для сравнения примитивов.
Джей
3
@ Джей нет, я это понимаю. но первое, что меня смущает, это то, почему первый возвращает true, а второй возвращает false, используя тот же метод сравнения ==. во всяком случае, теперь ясно.
ДНР
1
Примечание: дело не только в том, что Integer «может» кэшироваться между -128 и 127. Должно быть, согласно JLS 5.1.7 . Он может быть кэширован за пределами этого диапазона, но не обязательно (и часто нет).
ишавит
127

IntegerКласс имеет статический кэш, который хранит 256 специальных Integerобъектов - по одному для каждого значения между -128 и 127. Имея это в виду, рассмотрим разницу между этими тремя.

new Integer(123);

Это (очевидно) делает совершенно новый Integer объект.

Integer.parseInt("123");

Это возвращает int примитивное значение после анализа String.

Integer.valueOf("123");

Это сложнее, чем другие. Это начинается с разбора String. Затем, если значение находится в диапазоне от -128 до 127, он возвращает соответствующий объект из статического кэша. Если значение находится за пределами этого диапазона, оно вызывает new Integer()и передает значение, чтобы вы получили новый объект.

Теперь рассмотрим три выражения в вопросе.

Integer.valueOf("127")==Integer.valueOf("127");

Это возвращает true, потому что Integerзначение, равное 127, извлекается дважды из статического кэша и сравнивается с самим собой. Здесь Integerзадействован только один объект, поэтому он возвращается true.

Integer.valueOf("128")==Integer.valueOf("128");

Это возвращает false, потому что 128 не находится в статическом кэше. Таким образом, новое Integerсоздается для каждой стороны равенства. Так как есть два разных Integerобъекта, и ==для объектов возвращается, только trueесли обе стороны - один и тот же объект, это будет false.

Integer.parseInt("128")==Integer.valueOf("128");

Это сравнивает примитивное intзначение 128 слева с вновь созданным Integerобъектом справа. Но поскольку нет смысла сравнивать и intс Integer, Java будет автоматически распаковывать их Integerперед выполнением сравнения; так что в итоге вы сравниваете intс int. Поскольку примитив 128 равен самому себе, это возвращается true.

Дауд ибн Карим
источник
13

Позаботьтесь о возврате значений из этих методов. Метод valueOf возвращает экземпляр Integer:

public static Integer valueOf(int i)

Метод parseInt возвращает целочисленное значение (тип примитива):

public static int parseInt(String s) throws NumberFormatException

Объяснение для сравнения:

В целях экономии памяти два экземпляра объектов-оболочек всегда будут ==, если их примитивные значения одинаковы:

  • логический
  • Байт
  • Символ от \ u0000 до \ u007f (7f - 127 в десятичном виде)
  • Короткие и целые от -128 до 127

Когда == используется для сравнения примитива с оберткой, обертка будет развернута, и сравнение будет примитивом примитива.

В вашей ситуации (согласно вышеуказанным правилам):

Integer.valueOf("127")==Integer.valueOf("127")

Это выражение сравнивает ссылки на один и тот же объект, поскольку оно содержит значение Integer в диапазоне от -128 до 127, поэтому оно возвращает true.

Integer.valueOf("128")==Integer.valueOf("128")

Это выражение сравнивает ссылки на разные объекты, поскольку они содержат целочисленные значения, отсутствующие в <-128, 127>, поэтому оно возвращает false.

Integer.parseInt("128")==Integer.valueOf("128")

Это выражение сравнивает значение примитива (левая сторона) и ссылку на объект (правая сторона), поэтому правая часть будет развернута, а его тип примитива будет сравнен с левым, поэтому он вернется true.

piobab
источник
3
Аналогичный вопрос: stackoverflow.com/questions/9824053/…
piobab
Можете ли вы предоставить URL для источника предложения?
Филзен
«... два экземпляра объектов-обёрток всегда будут ==, если их примитивные значения одинаковы ...» - абсолютно неверно. Если вы создадите два объекта-обертки с одинаковым значением, они не будут возвращать true при сравнении с ними ==, потому что это разные объекты.
Дауд ибн Карим
6

Кэши целых объектов между -128 и 127 из 256 Integer

Вы не должны сравнивать ссылки на объекты с == или ! = . Вы должны использовать. равен (..) вместо этого или лучше - используйте примитив int, а не Integer.

parseInt : анализирует строковый аргумент как десятичное целое число со знаком. Все символы в строке должны быть десятичными цифрами, за исключением того, что первый символ может быть знаком минус ASCII '-' ('\ u002D') для обозначения отрицательного значения. Полученное целочисленное значение возвращается точно так, как если бы аргумент и основание 10 были заданы в качестве аргументов метода parseInt (java.lang.String, int).

Возвращает объект Integer, содержащий значение, извлеченное из указанной строки при анализе с основанием, заданным вторым аргументом. Первый аргумент интерпретируется как представляющий целое число со знаком в основании, заданном вторым аргументом, точно так же, как если бы аргументы были переданы методу parseInt (java.lang.String, int). Результатом является объект Integer, представляющий целочисленное значение, указанное в строке.

эквивалентно

new Integer(Integer.parseInt(s, radix))

radix - основа, которая будет использоваться при интерпретации

так что если вы равны Integer.valueOf()для целого числа между

От -128 до 127 возвращает истину в вашем состоянии

для lesser than-128 и greater than127 это даетfalse

Nambi
источник
6

Чтобы дополнить данные ответы, также обратите внимание на следующее:

public class Test { 
    public static void main(String... args) { 
        Integer a = new Integer(129);
        Integer b = new Integer(129);
        System.out.println(a == b);
    }
}

Этот код также напечатает: false

Как заявил пользователь Jay в комментарии о принятом ответе, необходимо соблюдать осторожность при использовании оператора ==над объектами, здесь вы проверяете, являются ли обе ссылки одинаковыми, а это не так, потому что это разные объекты, хотя они представляют такое же значение. Чтобы сравнивать объекты, вы должны использовать equals вместо этого метод:

Integer a = new Integer(128);
Integer b = new Integer(128);
System.out.println(a.equals(b));

Это напечатает: true

Вы спросите: а зачем тогда печатать первую строку true? , Проверяя исходный код Integer.valueOfметода, вы можете увидеть следующее:

public static Integer valueOf(String s) throws NumberFormatException {
    return Integer.valueOf(parseInt(s, 10));
}

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);
}

Если параметр является целым числом между IntegerCache.low(по умолчанию -128) и IntegerCache.high(вычисляется во время выполнения с минимальным значением 127), то возвращается предварительно выделенный (кэшированный) объект. Поэтому, когда вы используете 127 в качестве параметра, вы получаете две ссылки на один и тот же кэшированный объект и trueсравниваете ссылки.

higuaro
источник