compareTo () против equals ()

118

При тестировании на равенство Stringв Java я всегда использовал, equals()потому что мне это кажется наиболее естественным методом для этого. В конце концов, его название уже говорит о том, для чего он предназначен. Однако мой коллега недавно сказал, что меня научили использовать compareTo() == 0вместо equals(). Это кажется мне неестественным (поскольку compareTo()предназначено для упорядочивания, а не сравнения на равенство) и даже несколько опасным (потому compareTo() == 0что не обязательно подразумевает равенство во всех случаях, хотя я знаю, что это так для String) для меня.

Он не знал, почему его учили использовать compareTo()вместо equals()for String, и я также не мог найти причины, почему. Это действительно вопрос личного вкуса, или есть реальная причина для любого метода?

Томас Лётцер
источник
9
Строго говоря, на уровне микрооптимизации, о котором мы никогда не должны преждевременно говорить, .equalsIgnoreCase()это самое быстрое сравнение, если оно уместно, иначе .equals()это то, что вы хотите.
1
Это старый вопрос, но он содержит самородок, который я хотел выделить: « compareTo() == 0не обязательно подразумевает равенство во всех случаях». Это абсолютно правильно! Это именно то , что фраза « в соответствии с» равными средствами в спецификации API Java, например , в Сравнительной спецификации класса. Например, метод сравнения String совместим с equals, но метод сравнения BigDecimal несовместим с equals.
Стюарт Маркс

Ответы:

105

Разница в том, что "foo".equals((String)null)возвращает false, а "foo".compareTo((String)null) == 0выдает исключение NullPointerException. Поэтому они не всегда взаимозаменяемы даже для струнных.

свиристель
источник
6
Думаю, вам тоже стоит упомянуть об этом. equals вычисляет хэш-код. Хэш-код двух разных строк, хотя и встречается редко, может совпадать. Но при использовании compareTo () он проверяет строки посимвольно. Таким образом, в этом случае две строки вернут истину тогда и только тогда, когда их фактическое содержимое равно.
Ashwin
30
Как вы думаете, почему equals вычисляет хэш-код? Как видите, это не так: docjar.com/html/api/java/lang/String.java.html (строка 1013).
свиристель
3
Ты прав. Я думал, что equals и hashcode идут рука об руку (equals проверяет, имеют ли оба одинакового хэш-кода). Тогда почему необходимо переопределить оба этих метода, если вы решили переопределить любой из них.
Ashwin
3
Необходимо переопределить хэш-код, когда мы переопределяем равно, потому что, если класс использует коллекции на основе хешей, включая HashMap, HashSet и Hashtable, класс не будет работать должным образом
varunthacker 06
4
@Ashwin Это необходимо, потому что, если .equals возвращает истину, хэш-коды также должны быть равны. Однако, если хэш-коды равны, это не означает, что .equals должен возвращать истину
Алан,
33

Два основных отличия заключаются в следующем:

  1. equalsпримет любой объект в качестве параметра, но compareToбудет принимать только строки.
  2. equalsтолько сообщает вам, равны они или нет, но compareToдает информацию о лексикографическом сравнении строк.

Я взглянул на код класса String , и алгоритм в compareTo и equals выглядит в основном одинаково. Я считаю, что его мнение было всего лишь вопросом вкуса, и я согласен с вами - если все, что вам нужно знать, это равенство строк, а не то, какая из них будет первой лексикографически, то я бы использовал equals.

Калеб Браси
источник
26

При сравнении на равенство вы должны использовать equals(), потому что это ясно выражает ваше намерение.

compareTo()имеет дополнительный недостаток: он работает только с объектами, реализующими Comparableинтерфейс.

Это применимо в целом, а не только для струнных.

starblue
источник
18

compareToдолжен сделать больше работы, если струны имеют разную длину. equalsможет просто вернуть false, но compareToвсегда должен проверять достаточное количество символов, чтобы найти порядок сортировки.

robinr
источник
10

compareTo()применяется не только к строкам, но и к любому другому объекту, поскольку compareTo<T>принимает общий аргумент T. String - это один из классов, которые реализовали compareTo()метод путем реализации Comparableинтерфейса. (compareTo () - это метод для сопоставимого интерфейса). Таким образом, любой класс может свободно реализовать интерфейс Comparable.

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

апурва джадхав
источник
10

В контексте строки:
compareTo: лексикографически сравнивает две строки.
равно: сравнивает эту строку с указанным объектом.

compareTo сравнивает две строки по их символам (с одинаковым индексом) и возвращает целое число (положительное или отрицательное) соответственно.

String s1 = "ab";
String s2 = "ab";
String s3 = "qb";
s1.compareTo(s2); // is 0
s1.compareTo(s3); // is -16
s3.compareTo(s1); // is 16
VirtualLogic
источник
7

equals () может быть более эффективным, чем compareTo () .

Очень важное различие между compareTo и equals:

"myString".compareTo(null);  //Throws java.lang.NullPointerException
"myString".equals(null);     //Returns false

equals () проверяет, совпадают ли два объекта или нет, и возвращает логическое значение.

compareTo () (из интерфейса Comparable) возвращает целое число. Он проверяет, какой из двух объектов «меньше», «равен» или «больше» другого. Не все объекты можно упорядочить логически, поэтому метод compareTo () не всегда имеет смысл.

Обратите внимание, что equals () не определяет порядок между объектами, как compareTo ().

Теперь я советую вам просмотреть исходный код обоих методов, чтобы сделать вывод, что equals предпочтительнее, чем compareTo, который включает некоторые математические вычисления.

Джаймин Патель
источник
5

Похоже, что оба метода в значительной степени делают одно и то же, но метод compareTo () принимает String, а не Object, и добавляет некоторые дополнительные функции поверх обычного метода equals (). Если все, о чем вы заботитесь, - это равенство, тогда метод equals () - лучший выбор просто потому, что он имеет больше смысла для следующего программиста, который взглянет на ваш код. Разница во времени между двумя разными функциями не должна иметь значения, если вы не перебираете огромное количество элементов. CompareTo () действительно полезен, когда вам нужно знать порядок строк в коллекции или когда вам нужно знать разницу в длине между строками, начинающимися с одной и той же последовательности символов.

источник: http://java.sun.com/javase/6/docs/api/java/lang/String.html

Скотт М.
источник
5

equals() должен быть методом выбора в случае OP.

Глядя на реализацию equals()и compareTo()в java.lang.String в grepcode , мы можем легко увидеть, что equals лучше, если мы просто озабочены равенством двух строк:

equals():

1012   public  boolean равно ( объект anObject) { 
1013 if ( this == anObject) {
1014 return true ;
1015 }
1016 if ( экземпляр объекта String ) {
1017 String anotherString = ( String ) anObject;
1018 int n = количество;
1019 if (n == anotherString.count) {
1020 char v1 [] = значение;
1021 char v2 [] = anotherString.value;
1022 интi = смещение;
1023 int j = anotherString.offset;
1024 while (n--! = 0) {
1025 if (v1 [i ++]! = V2 [j ++])
1026 return false ;
1027 }
1028 return true ;
1029 }
1030 }
1031 возврат false ;
1032 }

и compareTo():

1174   public  int compareTo ( String anotherString) { 
1175 int len1 = count;
1176 int len2 = anotherString.count;
1177 int n = математика. min (len1, len2);
1178 char v1 [] = значение;
1179 char v2 [] = anotherString.value;
1180 int я = смещение;
1181 int j = anotherString.offset;
1183, если (i == j) {
1184 int k = i;
1185 int lim = n + я;
1186 в то время как (k <lim) {
1187 символов c1 = v1 [k];
1188 char c2 = v2 [k];
1189 if (c1! = C2) {
1190 return c1 - c2;
1191 }
1192 k ++;
1193 }
1194 } else {
1195 while (n--! = 0) {
1196 char c1 = v1 [i ++];
1197 символов c2 = v2 [j ++];
1198 if (c1! = C2) {
1199 return c1 - c2;
1200 }
1201 }
1202 }
1203 возврат len1 - len2;
1204 }

Когда одна из строк является префиксом другой, производительность compareTo()ухудшается, так как ей все еще необходимо определять лексикографический порядок, при этом equals()больше не будет беспокоиться и немедленно вернуть false.

На мой взгляд, мы должны использовать эти два по назначению:

  • equals() чтобы проверить равенство, и
  • compareTo() найти лексический порядок.
ФРК
источник
3

equals () проверяет, равны ли две строки или нет, и выдает логическое значение. compareTo () проверяет, равен ли строковый объект другому строковому объекту, больше или меньше. Он дает результат как: 1, если строковый объект больше 0, если оба равны -1, если строка меньше другой строки

э:

String a = "Amit";
String b = "Sumit";
String c = new String("Amit");
System.out.println(a.equals(c));//true
System.out.println(a.compareTo(c)); //0
System.out.println(a.compareTo(b)); //1
Анкит Униял
источник
2

При переопределении compareTo в Java необходимо помнить об определенных вещах, например, Compareto должно быть согласовано с equals, а вычитание не должно использоваться для сравнения целочисленных полей, поскольку они могут переполняться. проверьте « Что нужно помнить при переопределении компаратора в Java» .

Сима Киран
источник
2

Это эксперимент по некромантии :-)

В большинстве ответов сравнивается производительность и различия API. Они упускают из виду тот фундаментальный момент, что две операции просто имеют разную семантику.

Ваша интуиция верна. x.equals (y) не является взаимозаменяемым с x.compareTo (y) == 0. Первый сравнивает идентичность, а другой сравнивает понятие «размер». Это правда, что во многих случаях, особенно с примитивными типами, эти два совпадают.

Общий случай таков:

Если x и y идентичны, они имеют одинаковый «размер»: если x.equals (y) истинно => x.compareTo (y) равно 0.

Однако, если x и y имеют одинаковый размер, это не означает, что они идентичны.

Если x.compareTo (y) равен 0, это не обязательно означает, что x.equals (y) истинно.

Убедительным примером того, что идентичность отличается от размера, могут быть комплексные числа. Предположим, что сравнение производится по их абсолютному значению. Итак, учитывая два комплексных числа: Z1 = a1 + b1 * i и Z2 = a2 + b2 * i:

Z1.equals (z2) возвращает истину тогда и только тогда, когда a1 = a2 и b1 = b2.

Однако Z1.compareTo (Z2) возвращает 0 и бесконечное количество пар (a1, b1) и (a2, b2), если они удовлетворяют условию a1 ^ 2 + b1 ^ 2 == a2 ^ 2 + b2 ^ 2.

Виталий
источник
1
x.equals (y) не означает идентичность, это означает равенство. Идентичность сравнивается с использованием x == y для определяемых пользователем типов в Java.
Фернандо Пелличчони
2

Equals может быть более эффективным, чем compareTo.

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

Более того, если это один и тот же объект (тождественное равенство, а не логическое равенство), он также будет более эффективным.

Если бы они также реализовали кеширование hashCode, было бы еще быстрее отклонять не-равные, если их hashCode не совпадает.

Гопал Раджпурохит
источник
2

String.equals()требует вызова instanceofоператора, а compareTo()не требует. Мой коллега заметил большое падение производительности, вызванное чрезмерным количеством instanceofвызовов equals()метода, однако мой тест оказался compareTo()лишь немного быстрее.

Однако я использовал Java 1.6. В других версиях (или у других поставщиков JDK) разница может быть больше.

Тест сравнивал каждую строку в массивах из 1000 элементов, повторяемых 10 раз.

Дунайский моряк
источник
1
Я предполагаю, что вы, должно быть, использовали очень короткие строки одинаковой длины и с большим разнообразием для первого символа, чтобы получить результат, где compareTo()быстрее, чем equals()? Но для большинства наборов реальных данных equals()это намного быстрее, чем compareTo() == 0. equals()светится, если строки имеют общий префикс, но разную длину, или если они на самом деле являются одним и тем же объектом.
x4u 03
Что ж, мой тест был против гипотезы, что instanceof убивает производительность, поэтому строки действительно были короткими.
Danubian Sailor
Если ваша строка случайна и в основном различается по длине, то более очевидным выбором должен быть метод equals () вместо compareTo (), поскольку в большинстве случаев он немедленно возвращает false, если они не равны по длине.
sactiw
1
  1. equalsможет принимать любой объект в качестве параметра, но compareToможет принимать только строку.

  2. когда cometo null, compareToвызовет исключение

  3. когда вы хотите узнать, где происходит разница, вы можете использовать compareTo.

тигренок
источник
compareTo может принимать любой объект в качестве аргумента. Как сказал @apurva jadhav выше, сопоставимый принимает общий аргумент. Если вы реализуете comaparable как Comparable <String>, тогда вам разрешено использовать только строки.
Gaurav Kumar
1
  • equals: требуется для проверки равенства и ограничения дублирования. Многие классы библиотеки Java используют это, если хотят найти дубликаты. например, HashSet.add(ob1)будет добавляться только в том случае, если этого не существует. Поэтому, если вы расширяете такие классы, переопределите equals().

  • compareTo: требуется для заказа элемента. Опять же, для стабильной сортировки вам требуется равенство, поэтому возвращается 0.

Диллип Рут
источник
0

Равно -

1- Переопределите метод GetHashCode, чтобы тип мог правильно работать в хеш-таблице.

2- Не создавайте исключения при реализации метода Equals. Вместо этого верните false для нулевого аргумента.

3-

  x.Equals(x) returns true.

  x.Equals(y) returns the same value as y.Equals(x).

  (x.Equals(y) && y.Equals(z)) returns true if and only if x.Equals(z) returns true.

Последовательные вызовы x.Equals (y) возвращают одно и то же значение, если объект, на который ссылаются x и y, не изменяется.

x.Equals(null) returns false.

4- Для некоторых типов объектов желательно иметь тест Equals на равенство значений вместо ссылочного равенства. Такие реализации Equals возвращают true, если два объекта имеют одинаковое значение, даже если они не являются одним и тем же экземпляром.

Например -

   Object obj1 = new Object();
   Object obj2 = new Object();
   Console.WriteLine(obj1.Equals(obj2));
   obj1 = obj2; 
   Console.WriteLine(obj1.Equals(obj2)); 

Вывод :-

False
True

в то время как compareTo -

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

Он возвращается -

Меньше нуля - этот экземпляр предшествует obj в порядке сортировки. Ноль - этот экземпляр находится в той же позиции в порядке сортировки, что и объект obj. Больше нуля - этот экземпляр следует за obj в порядке сортировки.

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

Например, вы можете посетить здесь.

Поэтому я предлагаю лучше использовать Equals вместо compareTo.

Прабхат Джайн
источник
Нет никакого GetHashCode()метода. Вы имеете в виду hashCode()?
Маркиз Лорн,
0

«равно» сравнивает объекты и возвращает истину или ложь, а «сравнивать с» возвращает 0, если истинно, или число [> 0] или [<0], если ложно. Вот пример:

<!-- language: lang-java -->
//Objects Integer
Integer num1 = 1;
Integer num2 = 1;
//equal
System.out.println(num1.equals(num2));
System.out.println(num1.compareTo(num2));
//New Value
num2 = 3;//set value
//diferent
System.out.println(num1.equals(num2));
System.out.println(num1.compareTo(num2));

Полученные результаты:

num1.equals(num2) =true
num1.compareTo(num2) =0
num1.equals(num2) =false
num1.compareTo(num2) =-1

Документация Сравните с: https://docs.oracle.com/javase/7/docs/api/java/lang/Comparable.html

Документация равна: https://docs.oracle.com/javase/7/docs/api/java/lang/Object.html#equals(java.lang.Object)

Дэвид Хакро
источник
0

Здесь важна одна вещь при использовании compareTo()over, equals()который compareToработает для классов, реализующих интерфейс Comparable, иначе он вызовет файл NullPointerException. Stringclass реализует интерфейс Comparable, а StringBufferне, следовательно, вы можете использовать его "foo".compareTo("doo")в Stringобъекте, но не в StringBufferобъекте.

Pratik
источник
0
String s1 = "a";
String s2 = "c";

System.out.println(s1.compareTo(s2));
System.out.println(s1.equals(s2));

Это печатает -2 и ложь

String s1 = "c";
String s2 = "a";
System.out.println(s1.compareTo(s2));
System.out.println(s1.equals(s2));

Это печатает 2 и ложь

String s1 = "c";
String s2 = "c";
System.out.println(s1.compareTo(s2));
System.out.println(s1.equals(s2));

Это печатает 0 и истина

equals возвращает логическое значение тогда и только тогда, когда совпадают обе строки.

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

Дипак Сельвакумар
источник
-1

Я считаю equalsи equalsIgnoreCaseметоды Stringвозврата trueи falseкоторые полезны, если вы хотите сравнить значения строкового объекта, но в случае реализации compareToи compareToIgnoreCaseметоды возвращают положительное, отрицательное и нулевое значение, которое будет полезно в случае сортировки.

Пракхар Адитья
источник