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

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

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.

Ответ 1

Здесь поразительное различие.

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:

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

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

Ответ 2

Класс 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.

Ответ 3

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

public static Integer valueOf(int i)

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

public static int parseInt(String s) throws NumberFormatException

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

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

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

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

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

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

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

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

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

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

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

Ответ 4

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

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

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

Ответ 5

Целочисленные объекты кэшируют между -128 и 127 из 256 Integer

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

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

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

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

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

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

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

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

для lesser than -128 и greater than 127 он дает false