Сравнение символов, целых чисел и подобных типов в Java: используйте equals или ==?

Я хотел кое-что удостовериться в Java: если у меня есть символ, целое число или длинное число и тому подобное, должен ли я использовать equals или == достаточно?

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

Моя интуиция состоит в том, чтобы использовать equals, но я хочу убедиться, что не теряю производительность.


person Uri    schedule 09.04.2009    source источник
comment
никогда не предполагайте ничего о производительности - проверьте ее. возможно, == и .equals будут иметь примерно одинаковую производительность, и в этом случае вопрос спорный.   -  person TofuBeer    schedule 10.04.2009
comment
Единственное, что можно предположить о производительности: не имеет значения, если неправильный код быстрее. :-)   -  person Darron    schedule 10.04.2009
comment
Вот в этом я и хотел убедиться.   -  person Uri    schedule 10.04.2009
comment
Что касается я знаю, что со строками нет никаких гарантий, что существует только один экземпляр каждой уникальной строки: если по какой-то причине вам это понадобится, см. String#intern().   -  person Arjan    schedule 22.05.2013


Ответы (9)


РЕДАКТИРОВАТЬ: Спецификация дает некоторые гарантии для преобразования бокса. Из раздела 5.1.7:

Если упаковываемое значение p равно true, false, byte, char в диапазоне от до , или int или короткое число между -128 и 127, то пусть r1 и r2 будут результатами любых двух преобразований упаковки с. Всегда так, что r1 == r2.

Имейте в виду, что реализация может использовать больший пул.

Я бы действительно избегал написания кода, основанного на этом. Не потому, что это может привести к сбою, а потому, что это неочевидно — мало кто так хорошо знает спецификацию. (Ранее я думал, что это зависит от реализации.)

Вы должны использовать equals или сравнить базовые значения, т.е.

if (foo.equals(bar))

or

if (foo.intValue() == bar.intValue())

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

person Jon Skeet    schedule 09.04.2009
comment
IIRC, JLS говорит как минимум от -128 до 127 для byte, short и int, с эквивалентом для char, но не для long, float или short. IIRC, 6u14 и предыдущие выпуски производительности позволяют настраивать размер пула через системное свойство. - person Tom Hawtin - tackline; 10.04.2009
comment
Вау, ты прав. Я мог бы поклясться, что это давало свободу действий в пул или не в пул. Будет редактировать. - person Jon Skeet; 10.04.2009
comment
@ Том, ты дважды упомянул короткий. На самом деле есть пул одного размера для коротких и длинных. Можно сказать, что Boolean всегда объединялся. - person Peter Lawrey; 10.04.2009

Если вы хотите сравнить что-либо о значении любого объекта, используйте .equals().

Даже (и особенно), если эти объекты являются примитивными типами оболочек Byte, Character, Short, Integer, Long, Float, Double и Boolean.

"==" только когда-либо сравнивает идентификатор объекта, и вы очень, очень редко это то, что вам нужно. И де-факто никогда не то, что вы хотите с примитивными обертками.

Используйте == только в одном из этих двух сценариев:

  1. все значения, участвующие в сравнении, являются примитивными типами (и желательно не числами с плавающей запятой)
  2. вы действительно хотите знать, относятся ли две ссылки к одному и тому же объекту (включая сравнение enums, потому что там значение привязано к идентификатору объекта)
person Joachim Sauer    schedule 09.04.2009
comment
сценарий 3: вы сравниваете экземпляры enum или какой-либо другой неизменяемый тип, который управляет своими экземплярами таким образом, чтобы == был безопасным. - person Eddie; 10.04.2009
comment
ваш сценарий 3 такой же, как сценарий 2 - person TofuBeer; 10.04.2009
comment
@TofuBeer: технически да, но многие люди так не думают. - person Eddie; 10.04.2009

//Quick test
public class Test {
  public static void main(String[] args) {
    System.out.println("Are they equal? "+ (new Long(5) == new Long(5)));
  }
}

Выход:

"Они равны? 0"

Отвечать:

Нет, они не равны. Вы должны использовать .equals или сравнивать их примитивные значения.

person Brandon Yarbrough    schedule 09.04.2009
comment
Как насчет Long.valueOf(5) == Long.valueOf(5) ? - person Maurice Perry; 10.04.2009
comment
new Long(...) определенно даст вам другую ссылку каждый раз. Ничего удивительного. Однако Long.valueOf(...) будет объединять ссылки. Как сказал Морис, в данном случае ваш тест не подходит. - person Varkhan; 10.04.2009
comment
поскольку вы можете не знать, как был получен Long, использование == почти никогда не будет безопасным. - person TofuBeer; 10.04.2009
comment
Действительно, TofuBeer является правильным. Конечно, есть способы получить два фактически идентичных Лонга, особенно как он отметил в своем ответе. Это все еще ужасная идея полагаться на эти способы, поскольку, если вы позже измените эти Лонги, вы не будете думать о том, как изменился способ их сравнения на равенство. - person Brandon Yarbrough; 10.04.2009
comment
CaptainAwesomePants нужно показать только один случай, в котором == терпит неудачу, чтобы доказать, что вы не можете повсеместно полагаться на ==, даже если в некоторых случаях это работает. - person Kip; 10.04.2009

Спецификация языка Java 5.1.7 :

Если упаковываемое значение p равно true, false, byte, char в диапазоне от до , или int или короткое число между -128 и 127, то пусть r1 и r2 будут результатами любых двух преобразований упаковки с. Всегда так, что r1 == r2.

а также:

Обсуждение

В идеале упаковка заданного примитивного значения p всегда будет давать идентичную ссылку. На практике это может оказаться неосуществимым при использовании существующих методов реализации. Приведенные выше правила являются прагматичным компромиссом. Последний пункт выше требует, чтобы определенные общие значения всегда были упакованы в неразличимые объекты. Реализация может кэшировать их, лениво или нетерпеливо.

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

Это гарантирует, что в большинстве распространенных случаев поведение будет желаемым, без чрезмерного снижения производительности, особенно на небольших устройствах. Реализации с меньшим объемом памяти могут, например, кэшировать все символы и короткие, а также целые и длинные значения в диапазоне от -32 КБ до +32 КБ.

Итак, в некоторых случаях == будет работать, во многих других - нет. Всегда используйте .equals, чтобы быть в безопасности, поскольку вы не можете предоставить (как правило), как были получены экземпляры.

Если скорость является фактором (большинство .equals начинаются со сравнения == или, по крайней мере, должны), И вы можете гарантировать, как они были распределены И они соответствуют указанным выше диапазонам, тогда == безопасно.

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

person TofuBeer    schedule 09.04.2009

Реализации метода equals(Object o) почти всегда начинаются с

if(this == o) return true;

поэтому использование equals, даже если == истинно, на самом деле не сильно влияет на производительность.

Я рекомендую всегда* использовать метод equals для объектов.

* Конечно, очень редко вам не следует следовать этому совету.

person Skip Head    schedule 09.04.2009

Общий ответ: нет, вам не гарантируется, что для одного и того же числового значения объекты Long будут одинаковыми (даже если вы ограничитесь использованием Long.valueOf()).

Однако вполне возможно, что вы получите улучшение производительности, если сначала попытаетесь проверить равенство ссылок (используя ==), а затем, в случае неудачи, попробуете equals(). Все зависит от сравнительных затрат на дополнительный тест == и вызов метода... Ваш пробег может варьироваться, но стоит попробовать простой циклический тест, чтобы увидеть, что лучше.

person Varkhan    schedule 09.04.2009

Стоит отметить, что автоматически упакованные значения будут использовать объект пула, если они доступны. Вот почему (Целое число) 0 == (Целое число) 0, но (Целое число) 128 != (Целое число) 128 для Java 6u13

person Peter Lawrey    schedule 09.04.2009

Мне нравится видеть результат визуально:

  public static void main(String[] args)
  {
        Integer a = 126; //no boxed up conversion, new object ref
        Integer b = 126; //no boxed up conversion, re-use memory address
        System.out.println("Are they equal? " + (a == b)); // true
        Integer a1 = 140; //boxed up conversion, new object
        Integer b1 = 140; //boxed up conversion, new object
        System.out.println("Are they equal? " + (a1 == b1)); // false
        System.out.println("Are they equal? " + (new Long(5) == new Long(5))); // false
  }
person djangofan    schedule 07.12.2016

== сравнивает ссылку на объект, а equals(Object obj) сравнивает на равенство объектов. Если может существовать более одного экземпляра объекта equals, вы должны использовать equals для сравнения на равенство.

Примеры:

Integer i1 = new Integer(12345);
Integer i2 = new Integer(12345);

это разные экземпляры объекта, но они равны в соответствии с равенством Integer, поэтому вы должны использовать equals(Object obj)

public enum Gender {
    MALE, FEMALE;
}

в этом случае будет существовать только один экземпляр FEMALE, поэтому == можно безопасно использовать.

person Steve Kuo    schedule 09.04.2009