Что именно делает сравнение целых чисел с ==?

РЕДАКТИРОВАТЬ: Хорошо, хорошо, я неправильно прочитал. Я не сравниваю int с Integer. Верно подмечено.

В моей книге SCJP говорится:

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

Таким образом, вы могли бы подумать, что этот код напечатает true:

    Integer i1 = 1; //if this were int it'd be correct and behave as the book says.
    Integer i2 = new Integer(1);
    System.out.println(i1 == i2);

но он печатает false.

Кроме того, согласно моей книге, это должно печатать true:

Integer i1 = 1000; //it does print `true` with i1 = 1000, but not i1 = 1, and one of the answers explained why.
Integer i2 = 1000;
System.out.println(i1 != i2);

Неа. Это false.

Что дает?


person Bad Request    schedule 11.09.2010    source источник
comment
Видите ли, я не уверен, то ли эта часть книги написана плохо, то ли я идиот, но я просто не могу понять, в чем там говорится причина. Моя лучшая попытка понять, что == обрабатывается иначе, чем !=. Возможно, == отражает равенство значений (глубокое равенство), но != нет. В любом случае, то, что написано в книге, не происходит в реальной жизни.   -  person Bad Request    schedule 11.09.2010
comment
Нет, проблема в данном случае заключается в двух типах, которые вы сравниваете. == и != ведут себя одинаково.   -  person EboMike    schedule 11.09.2010
comment
Одного этого примера достаточно, чтобы заставить меня думать, что Java — это какая-то больная шутка, а не настоящий язык программирования.   -  person dsimcha    schedule 11.09.2010
comment
@dsimcha - это довольно неубедительное заявление. У вас может быть такая же проблема в C, если вам нужно сравнить два целочисленных указателя - это просто более явно.   -  person EboMike    schedule 11.09.2010
comment
Ваш второй пример печатает true. См. на ideone.   -  person NullUserException    schedule 11.09.2010
comment
это довольно запутанно. бокс очень полезен и безопасен. распаковки нет. всегда помните о типах данных в таких выражениях.   -  person irreputable    schedule 11.09.2010


Ответы (7)


Integer i1 = 1;
Integer i2 = new Integer(1);
System.out.println(i1 == i2);

Когда вы присваиваете 1 i1, это значение помещается в рамку, создавая объект Integer. Затем сравнение сравнивает две ссылки на объекты. Ссылки неравны, поэтому сравнение не удается.

Integer i1 = 100;
Integer i2 = 100;
System.out.println(i1 != i2);

Поскольку они инициализируются константами времени компиляции, компилятор может интернировать их и заставляет оба указывать на один и тот же объект Integer.

(Обратите внимание, что я изменил значения с 1000 на 100. Как указывает @NullUserException, интернируются только небольшие целые числа.)


Вот это действительно интересный тест. Посмотрим, сможешь ли ты это понять. Почему первая программа печатает true, а вторая false? Используя свои знания о боксе и анализе времени компиляции, вы сможете понять это:

// Prints "true".
int i1 = 1;
Integer i2 = new Integer(i1);
System.out.println(i1 == i2);

// Prints "false".
int i1 = 0;
Integer i2 = new Integer(i1);
i1 += 1;
System.out.println(i1 == i2);

Если вы понимаете вышеизложенное, попробуйте предсказать, что напечатает эта программа:

int i1 = 0;
i1 += 1;
Integer i2 = new Integer(i1);
System.out.println(i1 == i2);

(После того как вы догадались, запустите его и посмотрите!)

person John Kugelman    schedule 11.09.2010
comment
Это важно, поскольку интернируются только небольшие целые числа. — Имейте в виду, что Integer просто поддерживает внутренний кеш, который он использует (для указанных небольших значений), когда выполняется вызов valueOf; компилятор по-прежнему генерирует две строки invokestatic java/lang/Integer/valueOf(I)Ljava/lang/Integer; - person Tim Stone; 11.09.2010

Также обратите внимание, что более новые версии кэша Java Integers находятся в диапазоне от -128 до 127 (256 значений), что означает, что:

Integer i1, i2;

i1 = 127;
i2 = 127;
System.out.println(i1 == i2);

i1 = 128;
i2 = 128;
System.out.println(i1 == i2);

Напечатает true и false. (см. на ideone)

Мораль: во избежание проблем всегда используйте .equals() при сравнении двух объектов.

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

person NullUserException    schedule 11.09.2010
comment
Интересный! Я бы никогда не использовал == IRL, но мы говорим об очень садистском экзамене :) - person Bad Request; 11.09.2010
comment
+1 за очень важный момент. Я придираюсь и указываю, что это -128 для 127, хотя исходный код 1.6, поставляемый с Sun JDK, показывает, что они играют в игры с верхней границей (хотя она всегда не ниже 127). - person Tim Stone; 11.09.2010

Вы не сравниваете примитив с оболочкой. Вы сравниваете две оболочки (ссылочные типы). == сравнивает идентификатор объекта, который возвращает false, поскольку это разные объекты.

person dan04    schedule 11.09.2010
comment
Не всегда верно, меньшие целые значения кэшируются, как если бы вы вызвали Integer.valueOf. Так что вы фактически сравниваете одни и те же объекты. - person bvdb; 14.03.2019

Нет, я бы не подумал, что вывод кода верен, и вы сами ответили, почему.

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

и затем вы продолжили сравнение двух ссылок Integer, то есть сравнили адрес памяти i1 и i2. Вы хотели либо

Integer i1 = 1;
Integer i2 = new Integer(1);
System.out.println(i1.equals(i2));

Or

int i1 = 1;
Integer i2 = new Integer(1);
System.out.println(i1 == i2);
person David Souther    schedule 11.09.2010

Обратите внимание, что вы неправильно прочитали отрывок, который вы цитируете. Отрывок специально ограничивает это утверждение сравнениями, подобными этим:

int k = 1;
Integer l = new Integer(1);
System.out.println(l == k);
person user268396    schedule 11.09.2010

Начиная с Java 5.0 существует автоматическая упаковка и распаковка, что означает, что оболочки могут быть неявно преобразованы в примитивы и наоборот. Однако, если вы сравниваете два объекта Integer, вы все равно сравниваете две ссылки, и нет ничего, что могло бы вызвать автоматическую упаковку/распаковку. Если бы это было так, код, написанный в J2SE 1.4 и более ранних версиях, сломался бы.

person EboMike    schedule 11.09.2010

Предположим, пусть есть пример

Что будет на выходе этого программного кода?

public class autoboxing {
public static void main(String a args) {
Integer a = new Integer(127);
Integer b = new Integer(127);
Integer c = 127;
Integer d = 127;
Integer e = new Integer(200);
Integer f = new Integer(200);
Integer g = 200;
Integer h = 200;
System.out.println((a == b) + ' " + (c =-- d) + " " + (e==f)+ " "+ (g == h));

.

Когда вы создаете объект Integer с помощью нового оператора, он каждый раз возвращает новый объект. Когда вы сравниваете две ссылочные переменные с помощью оператора "==", если две ссылочные переменные ссылаются на два разных объекта, оператор "==" возвращает false.

So,

Выражения (a == b) и (e==f) возвращают false. Целочисленный класс кэширует значения от -128 до 127.

Когда вы сравниваете два целочисленных объекта с оператором «==», если эти два целочисленных объекта созданы с автоупаковкой, тогда будет вызываться метод value0f(int i).

ОТВЕТ: False True False False

Ниже приведена реализация этого метода

public static Integer value0f(int i) {
if (i >= IntegerCachedow && i <= IntegerCache.high)
return IntegerCache.cacheli + (-IntegerCachedow));
return new Integer(i);

Из приведенной выше реализации ниже приведены выводы

  1. Если значения двух объектов Integer находятся в диапазоне от -128 до 127, этот метод возвращает одинаковые значения. Итак, (c == d) возвращает true.

  2. Если значения двух объектов Integer находятся за пределами диапазона от -128 до 127, этот метод возвращает разные новые объекты Integer. Итак, (g == h) возвращает false

Подробнее о методе здесь: http://%22Why%20does%20this%20happen%22

person Abhijeet Rathore    schedule 27.08.2017