2009-10-03 5 views
149

Я знаю, что если сравнить коробочный примитивный Integer с константой, такой как:Как правильно сравнить два целых числа в Java?

Integer a = 4; 
if (a < 5) 

a будет автоматически распакованный и сравнение будет работать.

Однако, что происходит, когда вы сравниваете два в штучной упаковке Integers и хотите сравнить либо равенство, либо меньше или больше?

Integer a = 4; 
Integer b = 5; 

if (a == b) 

Будет ли над кодом выводиться проверка того, являются ли они одним и тем же объектом или будет ли он автоматически распаковываться в этом случае?

насчет:

Integer a = 4; 
Integer b = 5; 

if (a < b) 

?

+13

Ну, что случилось, когда вы пробовали? Что вы наблюдали? –

+17

@Bart Kiers: Явный эксперимент может только опровергнуть, а не доказывать, что происходит распаковка. Если использование '==' вместо 'equals' дает правильный результат, это может быть из-за того, что помещенные в номера номера интернированы или используются иным образом (как, например, оптимизация компилятора). Причина этого вопроса заключается в том, чтобы выяснить, что происходит внутри страны, а не то, что происходит. (По крайней мере, вот почему я здесь.) –

ответ

30

== все еще будет проверять равенство объектов. Однако легко одурачить:

Integer a = 10; 
Integer b = 10; 

System.out.println(a == b); //prints true 

Integer c = new Integer(10); 
Integer d = new Integer(10); 

System.out.println(c == d); //prints false 

Ваши примеры с неравенством будут работать, поскольку они не определены в объектах. Тем не менее, при сравнении ==, равномерность объекта будет по-прежнему проверяться. В этом случае, когда вы инициализируете объекты из примитива в штучной упаковке, используется тот же объект (для a и b). Это хорошая оптимизация, поскольку примитивные классы ящиков неизменяемы.

+0

Я понял, что проверяется равномерность объекта. У меня были некоторые странные результаты. Должен ли я заменить его на .equals()? Кроме того, чувствуете ли вы, что я должен оставить неравенство так, как есть, или сделать это по-другому? – 2009-10-03 21:42:05

+0

Имеются некоторые неочевидные краевые случаи с автобоксированием. У меня есть IDE (Eclipse), чтобы покрасить все, что было в коробке красным цветом, это несколько раз избавило меня от ошибок. Если вы сравниваете два целых числа, используйте .equals, если вы хотите, чтобы ваши неравенства были ясными, напишите приведение в явном виде: if ((int) c <(int) d) ...; Вы также можете сделать: c.compareTo (d) <0 // === c

+11

И если вы измените числовые литералы на '200', оба теста напечатают' false'. –

210

Нет, == между Integer, Long и т.д. будет проверять равенство ссылок - т.е.

Integer x = ...; 
Integer y = ...; 

System.out.println(x == y); 

это проверит x и y относятся к же объекта, а не равных объектов.

Так

Integer x = new Integer(10); 
Integer y = new Integer(10); 

System.out.println(x == y); 

гарантируется для печати false. Интернирование «малых» autoboxed значений может привести к результатам каверзные:

Integer x = 10; 
Integer y = 10; 

System.out.println(x == y); 

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

Лично я хотел бы использовать:

if (x.intValue() == y.intValue()) 

или

if (x.equals(y)) 

Последние несколько менее эффективен - нет перегрузки для Integer.equals(Integer) так что придется делать проверку исполнения типа времени , тогда как первый использует тот факт, что мы уже знаем, что оба объекта: Integer s.

К счастью, compareTo знает о типах, так:

if (x.compareTo(y) < 0) 

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

Как вы говорите, для любого сравнения типа обертки (Integer, Long и т.д.) и числовые type (int, long и т. д.) значение типа обертки unboxed, и тест применяется к примитивным значениям.

Это происходит как часть двоичной цифровой рекламы (JLS section 5.6.2). Посмотрите документацию каждого отдельного оператора, чтобы узнать, применяется ли она. ! Например, из Документов == и = (JLS 15.21.1):

Если операнды оператора равенства оба из числового типа, или один имеют числовой типа, а другой конвертируется (§5.1.8) на числовой тип , двоичная цифровая рассылка - , выполняемая на операндах (§5.6.2).

и <, < =,> и> = (JLS 15.20.1)

Тип каждого из операндов числового оператора в сравнения должен быть тип, который является обратимым (§5.1 .8) - - примитивный числовой тип или ошибка времени компиляции. Двоичное числовое продвижение выполняется в операндах (§5.6.2). Если присвоенный тип операндов является int или длинным, то затем выполняется целочисленное сравнение со знаком ; если этот продвинутый тип равен float или double, то с плавающей точкой сравнение выполняется.

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

+0

Есть ли причина, по которой хотелось бы написать 'x.compareTo (y) <0' вместо' x

+0

@MaxNanasy: Не то, о чем я могу сразу подумать. –

+1

С точки зрения Java 1.6.27+ существует перегрузка на равных в классе Integer, поэтому она должна быть такой же эффективной, как и вызов .intValue(). Он сравнивает значения как примитивные int. – otterslide

4

Т.Л., др мое мнение заключается в использовании одинарного + для запуска распаковки на одном из операндов при проверке на значение равенства, а просто использовать математику операторов в противном случае. Обоснование следует следующим образом:

Уже упоминалось, что сравнение для сравнения Integer - это сравнение идентичности, которое обычно не то, что хочет программист, и что целью является сравнение значений; Тем не менее, я сделал немного science о том, как сделать это сравнение наиболее эффективно, как с точки зрения компактности, правильности и скорости кода.

я использовал обычную кучу методов:

public boolean method1() { 
    Integer i1 = 7, i2 = 5; 
    return i1.equals(i2); 
} 

public boolean method2() { 
    Integer i1 = 7, i2 = 5; 
    return i1.intValue() == i2.intValue(); 
} 

public boolean method3() { 
    Integer i1 = 7, i2 = 5; 
    return i1.intValue() == i2; 
} 

public boolean method4() { 
    Integer i1 = 7, i2 = 5; 
    return i1 == +i2; 
} 

public boolean method5() { // obviously not what we want.. 
    Integer i1 = 7, i2 = 5; 
    return i1 == i2; 
} 

и получил этот код после компиляции и декомпиляции:

public boolean method1() { 
    Integer var1 = Integer.valueOf(7); 
    Integer var2 = Integer.valueOf(5); 

    return var1.equals(var2); 
} 

public boolean method2() { 
    Integer var1 = Integer.valueOf(7); 
    Integer var2 = Integer.valueOf(5); 

    if (var2.intValue() == var1.intValue()) { 
     return true; 
    } else { 
     return false; 
    } 
} 

public boolean method3() { 
    Integer var1 = Integer.valueOf(7); 
    Integer var2 = Integer.valueOf(5); 

    if (var2.intValue() == var1.intValue()) { 
     return true; 
    } else { 
     return false; 
    } 
} 

public boolean method4() { 
    Integer var1 = Integer.valueOf(7); 
    Integer var2 = Integer.valueOf(5); 

    if (var2.intValue() == var1.intValue()) { 
     return true; 
    } else { 
     return false; 
    } 
} 

public boolean method5() { 
    Integer var1 = Integer.valueOf(7); 
    Integer var2 = Integer.valueOf(5); 

    if (var2 == var1) { 
     return true; 
    } else { 
     return false; 
    } 
} 

Как вы можете легко увидеть, метод 1 вызывает Integer.equals() (очевидно), методы 2-4 приводят к точно такой же код, разворачивая значения с помощью .intValue(), а затем сравнивая их напрямую, а метод 5 просто запускает сравнение идентичности, являясь неправильным способом сравнения значений.

Поскольку (как уже было упомянуто, например, JS) equals() несет накладные расходы (он должен делать instanceof и неконтролируемый бросок), методы 2-4 будет работать с точно такой же скоростью, noticingly лучше, чем метод 1 при использовании в жесткой петли, поскольку HotSpot вряд ли оптимизирует отливки & instanceof.

Это очень похоже с другими операторами сравнения (например, </>) - они будут вызывать распаковка, при использовании compareTo() не будут - но на этот раз, операция является весьма оптимизируемым по ГВ, так как intValue() просто метод геттера (прайм кандидата на оптимизацию).

На мой взгляд, редко используется версия 4 является самым кратким образом - каждый заправленный C/Java разработчик знает, что унарный плюс в большинстве случаев равна приведение к int/.intValue() - в то время как это может быть немного WTF момент для некоторых (в основном тех, кто не использовал унарный плюс в своей жизни), он, возможно, показывает намерение наиболее четко и наименее - он показывает, что нам нужно значение int одного из операндов, заставляя другое значение распаковываться как Что ж. Также неоспоримо наиболее похожа на обычную i1 == i2 сравнения используется для примитивных int значений.

Мой голос идет на i1 == +i2 & i1 > i2 стиль для Integer объектов, как по соображениям производительности & консистенции. Он также переносит код в примитивы, не изменяя ничего, кроме объявления типа. Использование названных методов похоже на введение семантического шума для меня, похоже на сильно критикуемый стиль bigInt.add(10).multiply(-3).

8

== проверяет равенство ссылок, однако при написании кода, как:

Integer a = 1; 
Integer b = 1; 

Java достаточно умна, чтобы повторно использовать тот же неизменны для a и b, так это правда: a == b. Любопытно, что я написал небольшой пример, чтобы показать, где Java прекращает оптимизацию таким образом:

public class BoxingLol { 
    public static void main(String[] args) { 
     for (int i = 0; i < Integer.MAX_VALUE; i++) { 
      Integer a = i; 
      Integer b = i; 
      if (a != b) { 
       System.out.println("Done: " + i); 
       System.exit(0); 
      } 
     } 
     System.out.println("Done, all values equal"); 
    } 
} 

Когда я скомпилировать и запустить этот (на моей машине), я получаю:

Done: 128 
+1

tl; dr -1 для ручной работы; http://stackoverflow.com/questions/15052216/how-large-is-the-integer-cache http: // stackoverflow.com/questions/20897020/why-integer-class-caching-values-in-range-128-to-127 http://stackoverflow.com/questions/3131136/integers-caching-in-java и т. подробно расскажите о том, что вы упомянули; лучше читать документы (или источник lib), чем создавать псевдо-тесты с риском высокой локальности результатов - вы не только полностью забыли о нижней границе кеша (т. е. -128 по умолчанию), а не только у вас есть один за другим (максимум 127, а не 128), – vaxquis

+0

, но у вас нет * полностью нет гарантии *, чтобы получить тот же результат на любом компьютере - так как вы легко * увеличиваете * размер кеша самостоятельно, YMMV. Кроме того, вопрос OP заключался в том, как правильно сравнить два целых числа * - вы вообще не ответили на него *. – vaxquis

+0

Я уважаю ваше мнение и восприятие здесь. Я думаю, что у нас есть принципиально разные подходы к CS. –

3

Вызов

if (a == b) 

Будет работать большую часть времени, но он не всегда будет работать, поэтому не используйте его.

Самый правильный способ сравнения двух классов Integer равенства, предполагая, что они называются «а» и «б» является вызов:

if(a != null && a.equals(b)) { 
    System.out.println("They are equal"); 
} 

Вы также можете использовать этот способ, который немного быстрее.

if(a != null && b != null && (a.intValue() == b.intValue())) { 
     System.out.println("They are equal"); 
    } 

На моей машине 99 миллионов операций заняли 47 секунд с использованием первого метода и 46 секунд с использованием второго метода. Вам нужно будет сравнить миллиарды значений, чтобы увидеть разницу.

Обратите внимание, что «a» может быть пустым, поскольку это объект. Сравнение таким образом не приведет к исключению нулевого указателя.

Для сравнения больше и меньше, используйте

if (a != null && b!=null) { 
    int compareValue = a.compareTo(b); 
    if (compareValue > 0) { 
     System.out.println("a is greater than b"); 
    } else if (compareValue < 0) { 
     System.out.println("b is greater than a"); 
    } else { 
      System.out.println("a and b are equal"); 
    } 
} else { 
    System.out.println("a or b is null, cannot compare"); 
} 
+0

'if (a == b)' работает только для небольших значений и не будет работать большую часть времени. – Tony

+0

Он работает до 127, поскольку это кеш-память Integer Java по умолчанию, которая гарантирует, что все номера до 127 имеют одинаковое ссылочное значение. Вы можете настроить кеш выше 127, если хотите, но просто не используйте == для обеспечения безопасности. – otterslide

-1

этот метод сравнивает два Integer с нулевой проверкой см тестов

public static boolean compare(Integer int1, Integer int2) { 
    if(int1!=null) { 
     return int1.equals(int2); 
    } else { 
     return int2==null; 
    } 
    //inline version: 
    //return (int1!=null) ? int1.equals(int2) : int2==null; 
} 

//results: 
System.out.println(compare(1,1));   //true 
System.out.println(compare(0,1));   //false 
System.out.println(compare(1,0));   //false 
System.out.println(compare(null,0));  //false 
System.out.println(compare(0,null));  //false 
System.out.println(compare(null,null));  //true 
+0

добавьте некоторое объяснение к вашему ответу. – RamPrakash

+2

Для этого я считаю, что лучше использовать метод 'Objects.equals (x, y)' вместо того, чтобы кататься самостоятельно. – ryvantage

Смежные вопросы