Чому порівняння == із 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.


6
Ціле число - об’єкт; якщо ви хочете порівнювати рівність, використовуйте .equals(), інакше всі ставки відключені.
Карл Дамгаард Асмуссен

6
@KarlDamgaardAsmussen Насправді тут я дійсно хочу перевірити, чи є вони посиланнями на один і той же об’єкт, і спочатку я не розумію, чому 127 128 повертає різний результат.
DnR

@DnR, якби Java була мовою зі стандартизованою специфікацією, я думаю, що це дозволить таким чином реалізовуватись або навіть наказувати невизначене поведінку.
Карл Дамгаард Асмуссен

1
@jszumski: Хоча в цьому питанні більше, ніж просто частина кешування. Крім того, пов'язана відповідь у кращому випадку є неповною - вона не зовсім детально описується, що кешовано та чому.
Макото

1
Для подальшого подальшого вивчення цієї дискусії, будь ласка, зверніться до цього мета-повідомлення .
Jeroen Vannevel

Відповіді:


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.


2
@ user3152527: Існує значна різниця - один вважається об'єктом, це означає, що ви можете викликати методи та взаємодіяти з нею в абстрактних структурах даних, наприклад List. Інше - це примітив, який є лише сирим значенням.
Макото

1
@ user3152527 Ви задали відмінне запитання (а в кращому випадку - німий). Але ви виправили це використовувати .equals, правда?
користувач2910265

3
Так, здається, що запитуючий не зрозумів основного факту в Java: Коли ви використовуєте "==" для порівняння двох об'єктів, ви перевіряєте, чи вони посилаються на один і той же об'єкт. Використовуючи "equals ()", ви тестуєте, чи мають вони однакове значення. Ви не можете використовувати "рівний" для порівняння примітивів.
Jay

3
@ Джей ні, я це розумію. але той, який мене спочатку збиває з пантелику, - це те, чому перший повертає істину, а другий повертає помилково, використовуючи той самий метод порівняння ==. у будь-якому випадку, зараз зрозуміло.
DnR

1
Ніт: це не лише те, що Integer "може" кешуватися між -128 та 127. Це має бути, згідно JLS 5.1.7 . Він може бути кешований за межами цього діапазону, але не повинен бути (а часто і не є).
yshavit

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

Це повертає істину, оскільки 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з a 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")

Цей вираз порівнює посилання на різні об'єкти, оскільки вони містять значення Integer не в <-128, 127>, тому він повертається false.

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

Цей вираз порівнює примітивне значення (ліва частина) та посилання на об'єкт (права рука), тому права рука буде розгорнута, а його примітивний тип буде порівняно з лівим, щоб він повернувся true.


3
Схожий питання: stackoverflow.com/questions/9824053 / ...
piobab

Чи можете ви надати URL-адресу джерела цитат?
Філцен

"... два екземпляри обгорткових об'єктів, завжди будуть ==, коли їх примітивні значення однакові ..." - абсолютно помилкові. Якщо ви створите два об'єкти обгортки з однаковим значенням, вони не повернуть справжнє порівняння з ==, оскільки це різні об'єкти.
Давуд ібн Карім

6

Цілі об'єкти кешуються між -128 і 127 з 256 Integer

Не слід порівнювати посилання на об'єкти з == або ! = . Ви повинні використовувати. дорівнює (..) замість цього, або краще - використовуйте примітивний int, а не Integer.

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

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

дорівнює

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

radix - радіакс, який слід використовувати при інтерпретації s

тож якщо ви дорівнює Integer.valueOf()цілому числу між

Від -128 до 127, вона повертає справжній стан

для lesser than-128 та greater than127 даєfalse


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

Як користувач Джей заявив у коментарі до прийнятої відповіді, слід бути обережним при використанні оператора ==на об'єктах, тут ви перевіряєте, чи обидві посилання однакові, що ні, тому що це різні об'єкти, хоча вони представляють саме однакове значення. Для порівняння об'єктів слід скористатися 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порівняння посилань.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.