CompareTo () vs. equals ()


118

Podczas testowania równości Stringw Javie zawsze używałem, equals()ponieważ wydaje mi się, że jest to najbardziej naturalna metoda. W końcu jego nazwa już mówi, co ma zrobić. Jednak mój kolega niedawno powiedział mi, że nauczono mnie używać compareTo() == 0zamiast equals(). Wydaje mi się to nienaturalne (co compareTo()ma na celu zapewnienie porządku, a nie porównywanie dla równości), a nawet trochę niebezpieczne (ponieważ compareTo() == 0niekoniecznie oznacza to równość we wszystkich przypadkach, chociaż wiem, że tak Stringjest dla mnie).

Nie wiedział, dlaczego nauczono go używać compareTo()zamiast equals()for String, a ja też nie mogłem znaleźć żadnego powodu dlaczego. Czy to naprawdę kwestia osobistego gustu, czy też istnieje prawdziwy powód dla którejkolwiek z metod?


9
Ściśle mówiąc, na poziomie mikrooptymalizacji, o którym nigdy nie powinniśmy mówić przedwcześnie, .equalsIgnoreCase()jest najszybszym porównaniem, jeśli jest właściwe, w przeciwnym razie .equals()jest to, czego chcesz.

1
Jest to stare pytanie, ale zawiera ono samorodek, który chciałem podkreślić: „ compareTo() == 0niekoniecznie oznacza równość we wszystkich przypadkach”. To jest absolutnie poprawne! Dokładnie to oznacza wyrażenie „zgodne z równymi” w specyfikacji interfejsu API języka Java, na przykład w specyfikacji klasy Comparable . Na przykład metoda porównania String jest zgodna z równymi, ale metoda porównania BigDecimal jest niespójna z równymi.
Stuart Marks

Odpowiedzi:


104

Różnica polega na tym, że "foo".equals((String)null)zwraca wartość false, gdy "foo".compareTo((String)null) == 0zgłasza wyjątek NullPointerException. Więc nie zawsze są wymienne, nawet w przypadku Strings.


6
Myślę, że powinieneś o tym wspomnieć. equals oblicza hashcode. Hashcode dwóch różnych ciągów, choć rzadki, może być taki sam. Ale kiedy używa compareTo (), sprawdza łańcuchy znak po znaku. W tym przypadku dwa łańcuchy zwróci prawdę wtedy i tylko wtedy, gdy ich rzeczywista zawartość będzie równa.
Ashwin

30
Jak myślisz, dlaczego to równa się oblicza kod skrótu? Widać, że tak nie jest: docjar.com/html/api/java/lang/String.java.html (wiersz 1013).
jemiołuszka

3
masz rację. Myślałem, że równa się i hashcode idą w parze (equals sprawdza, czy oba mają ten sam kod skrótu). Dlaczego więc konieczne jest zastąpienie obu tych metod, jeśli zdecydujesz się zastąpić którąkolwiek z nich.
Ashwin,

3
Konieczne jest przesłonięcie hashcode, gdy nadpisujemy równa się, ponieważ jeśli klasa używa kolekcji opartych na skrótach, w tym HashMap, HashSet i Hashtable, klasa nie będzie działać poprawnie
varunthacker

4
@Ashwin Jest to konieczne, ponieważ jeśli .equals zwraca true, hashcodes również powinny być równe. Jednak jeśli hashcodes są równe, nie oznacza to, że equals powinno zwracać wartość true
Alan

33

Dwie główne różnice to:

  1. equalsweźmie dowolny obiekt jako parametr, ale compareTotylko ciągi.
  2. equalsmówi tylko o tym, czy są równe, czy nie, ale compareTodaje informacje o tym, jak Ciągi są porównywane leksykograficznie.

Wziąłem okiem na kod klasy String , a algorytm w CompareTo i wynosi wygląda w zasadzie tak samo. Uważam, że jego opinia była tylko kwestią gustu i zgadzam się z tobą - jeśli wszystko, co musisz wiedzieć, to równość Strun, a nie to, która z nich jest leksykograficzna jako pierwsza, użyłbym equals.


26

Porównując pod kątem równości, powinieneś używać equals(), ponieważ wyraża to twoje intencje w jasny sposób.

compareTo()ma tę dodatkową wadę, że działa tylko na obiektach implementujących Comparableinterfejs.

Dotyczy to ogólnie, nie tylko dla Strings.


18

compareTomusi wykonać więcej pracy, jeśli struny mają różne długości. equalsmoże po prostu zwrócić wartość false, podczas gdy compareTozawsze musi sprawdzić wystarczającą liczbę znaków, aby znaleźć porządek sortowania.


10

compareTo()dotyczy nie tylko ciągów znaków, ale także każdego innego obiektu, ponieważ compareTo<T>przyjmuje argument ogólny T. String jest jedną z klas, która zaimplementowała compareTo()metodę poprzez implementację Comparableinterfejsu. (compareTo () jest metodą dla porównywalnego interfejsu). Więc każda klasa może zaimplementować interfejs porównywalny.

Ale compareTo()podaje porządek obiektów , używany zwykle do sortowania obiektów w porządku rosnącym lub malejącym, podczas gdy equals()mówi tylko o równości i mówi, czy są równe, czy nie.


10

W kontekście ciągów:
compareTo: porównuje dwa ciągi leksykograficznie.
equals: porównuje ten ciąg z określonym obiektem.

CompareTo porównuje dwa ciągi według ich znaków (w tym samym indeksie) i odpowiednio zwraca liczbę całkowitą (dodatnią lub ujemną).

String s1 = "ab";
String s2 = "ab";
String s3 = "qb";
s1.compareTo(s2); // is 0
s1.compareTo(s3); // is -16
s3.compareTo(s1); // is 16

7

equals () może być bardziej wydajne niż compareTo () .

Bardzo ważna różnica między compareTo i equals:

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

equals () sprawdza, czy dwa obiekty są takie same, czy nie, i zwraca wartość logiczną.

compareTo () (z interfejsu Comparable) zwraca liczbę całkowitą. Sprawdza, który z dwóch obiektów jest „mniejszy niż”, „równy” lub „większy od” drugiego. Nie wszystkie obiekty można logicznie uporządkować, więc metoda compareTo () nie zawsze ma sens.

Zauważ, że equals () nie definiuje kolejności między obiektami, co robi metoda compareTo ().

Teraz radzę ci przejrzeć kod źródłowy obu metod, aby stwierdzić, że równa się jest lepsza niż metoda compareTo, która wymaga pewnych obliczeń matematycznych.


5

Wygląda na to, że obie metody robią prawie to samo, ale metoda compareTo () pobiera String, a nie Object, i dodaje dodatkowe funkcje do normalnej metody equals (). Jeśli zależy ci tylko na równości, to metoda equals () jest najlepszym wyborem, po prostu dlatego, że ma więcej sensu dla następnego programisty, który przyjrzy się Twojemu kodowi. Różnica czasu między dwiema różnymi funkcjami nie powinna mieć znaczenia, chyba że przeglądasz ogromną liczbę elementów. Funkcja compareTo () jest naprawdę przydatna, gdy trzeba znać kolejność ciągów w kolekcji lub gdy trzeba poznać różnicę w długości między ciągami zaczynającymi się od tej samej sekwencji znaków.

źródło: http://java.sun.com/javase/6/docs/api/java/lang/String.html


5

equals() powinna być metodą z wyboru w przypadku PO.

Patrząc na implementację equals()i compareTo()w java.lang.String w grepcode , możemy łatwo zauważyć, że equals jest lepsze, jeśli interesuje nas tylko równość dwóch ciągów:

equals():

1012   public  boolean równa się ( Object anObject) { 
1013 if ( this == anObject) {
1014 return true ;
1015 }
1016 if (anObject instanceof String ) {
1017 String anotherString = ( String ) anObject;
1018 int n = liczba;
1019 if (n == anotherString.count) {
1020 char v1 [] = wartość;
1021 char v2 [] = anotherString.value;
1022 int i = offset;
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 return false ;
1032 }

i compareTo():

1174   public  int compareTo ( String anotherString) { 
1175 int len1 = count;
1176 int len2 = anotherString.count;
1177 int n = Math. min (len1, len2);
1178 char v1 [] = wartość;
1179 char v2 [] = anotherString.value;
1180 int i = offset;
1181 int j = anotherString.offset;
1183 jeśli (i == j) {
1184 int k = i;
1185 int lim = n + i;
1186 while (k <lim) {
1187 charc1 = 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 char c2 = v2 [j ++];
1198 if (c1! = C2) {
1199 return c1 - c2;
1200 }
1201 }
1202 }
1203 powrótlen1 - len2;
1204 }

Kiedy jeden z ciągów jest prefiksem innego, wydajność compareTo()jest gorsza, ponieważ nadal musi określić kolejność leksykograficzną, equals()nie martwiąc się więcej i natychmiast zwraca false.

Moim zdaniem powinniśmy używać tych dwóch zgodnie z przeznaczeniem:

  • equals() aby sprawdzić równość i
  • compareTo() znaleźć porządek leksykalny.

3

equals () sprawdza, czy dwa łańcuchy są równe, czy nie i zwraca wartość logiczną. CompareTo () sprawdza, czy obiekt string jest równy, większy lub mniejszy od innego obiektu typu string i daje wynik jako: 1 jeśli obiekt string jest większy 0 jeśli oba są równe -1 jeśli łańcuch jest mniejszy niż inny ciąg

eq:

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

To jest eksperyment z nekromancją :-)

Większość odpowiedzi dotyczy porównania wydajności i różnic w API. Pomijają podstawowy punkt, że te dwie operacje mają po prostu różną semantykę.

Twoja intuicja jest poprawna. x.equals (y) nie jest wymienne z x.compareTo (y) == 0. Pierwsza porównuje tożsamość, podczas gdy druga porównuje pojęcie „rozmiar”. Prawdą jest, że w wielu przypadkach, zwłaszcza w przypadku typów prymitywnych, te dwa elementy są dopasowane.

Ogólny przypadek jest taki:

Jeśli x i y są identyczne, mają ten sam `` rozmiar '': jeśli x.equals (y) jest prawdą => x.compareTo (y) wynosi 0.

Jeśli jednak x i y mają ten sam rozmiar, nie oznacza to, że są identyczne.

jeśli x.compareTo (y) jest równe 0, niekoniecznie oznacza, że ​​x.equals (y) jest prawdą.

Niezwykłym przykładem, w którym tożsamość różni się od rozmiaru, byłyby liczby zespolone. Załóżmy, że porównanie jest dokonywane według ich wartości bezwzględnej. A więc biorąc pod uwagę dwie liczby zespolone: ​​Z1 = a1 + b1 * i oraz Z2 = a2 + b2 * i:

Z1.equals (z2) zwraca prawdę wtedy i tylko wtedy, gdy a1 = a2 i b1 = b2.

Jednak Z1.compareTo (Z2) zwraca 0 dla i nieskończoną liczbę par (a1, b1) i (a2, b2), o ile spełniają warunek a1 ^ 2 + b1 ^ 2 == a2 ^ 2 + b2 ^ 2.


1
x.equals (y) nie oznacza tożsamości, oznacza równość. Tożsamość jest porównywana przy użyciu x == y dla typów zdefiniowanych przez użytkownika w Javie.
Fernando Pelliccioni

2

Równość może być bardziej wydajna niż metoda compareTo.

Jeśli długość sekwencji znaków w łańcuchu nie pasuje, nie ma możliwości, aby ciągi były równe, więc odrzucenie może być znacznie szybsze.

Co więcej, jeśli jest to ten sam przedmiot (równość tożsamości, a nie równość logiczna), będzie również bardziej wydajna.

Gdyby również zaimplementowali buforowanie hashCode, odrzucenie wartości różnej od równości mogłoby być jeszcze szybsze w przypadku, gdy ich hashCode nie pasuje.


2

String.equals()wymaga wywołania instanceofoperatora, podczas gdy compareTo()nie wymaga. Mój kolega zauważył duży spadek wydajności spowodowany nadmierną liczbą instanceofwywołań equals()metody, jednak mój test okazał compareTo()się tylko nieznacznie szybszy.

Używałem jednak Javy 1.6. W innych wersjach (lub u innych dostawców JDK) różnica może być większa.

W teście porównano każdy ciąg znaków w 1000-elementowych tablicach, powtórzonych 10 razy.


1
Wydaje mi się, że musiałeś użyć bardzo krótkich strun, wszystkich tej samej długości i z dużą różnorodnością przy pierwszym znaku, aby uzyskać wynik, gdzie compareTo()jest szybszy niż equals()? Ale w przypadku większości zestawów danych ze świata rzeczywistego equals()jest znacznie szybszy niż compareTo() == 0. equals()świeci, jeśli łańcuchy mają wspólny przedrostek, ale różne długości lub jeśli są w rzeczywistości tym samym obiektem.
x4u

Cóż, mój test był przeciwko hipotezy, że instancja zabija wydajność, więc struny były rzeczywiście krótkie.
Danubian Sailor

Jeśli twój ciąg jest losowy i przeważnie różni się długością, bardziej oczywistym wyborem powinna być metoda equals () zamiast metody compareTo (), ponieważ w większości przypadków natychmiast zwróci false, jeśli nie mają one równej długości.
sactiw

1
  1. equalsmoże przyjąć dowolny obiekt jako parametr, ale compareTomoże przyjąć tylko wartość String.

  2. gdy cometo null, compareTozgłosi wyjątek

  3. jeśli chcesz wiedzieć, gdzie występuje różnica, możesz użyć compareTo.


compareTo może przyjąć dowolny obiekt jako argument. Jak powiedział @apurva jadhav powyżej, porównanie wymaga ogólnego argumentu. Jeśli zaimplementujesz comaparable jako Comparable <String>, możesz używać tylko ciągów.
Gaurav Kumar

1
  • equals: wymagane do sprawdzania równości i ograniczania duplikatów. Wiele klas biblioteki Java korzysta z tego w przypadku, gdy chcą znaleźć duplikaty. np. HashSet.add(ob1)doda tylko wtedy, gdy to nie istnieje. Więc jeśli rozszerzasz niektóre klasy, takie jak ta, zastąp equals().

  • compareTo: wymagane do zamówienia elementu. Ponownie do stabilnego sortowania potrzebujesz równości, więc jest zwrot 0.


0

Równa się -

1 - Zastąp metodę GetHashCode, aby umożliwić poprawne działanie typu w tabeli skrótów.

2- Nie zgłaszaj wyjątku w implementacji metody Equals. Zamiast tego zwróć false dla pustego argumentu.

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.

Kolejne wywołania x.Equals (y) zwracają tę samą wartość, o ile obiekt, do którego odwołuje się x i y, nie jest modyfikowany.

x.Equals(null) returns false.

4- W przypadku niektórych rodzajów obiektów pożądane jest, aby zamiast równości referencyjnej mieć test równości pod kątem równości wartości. Takie implementacje Equals zwracają wartość true, jeśli dwa obiekty mają tę samą wartość, nawet jeśli nie są tym samym wystąpieniem.

Na przykład -

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

Wynik :-

False
True

podczas porównywania do -

Porównuje bieżące wystąpienie z innym obiektem tego samego typu i zwraca liczbę całkowitą wskazującą, czy bieżące wystąpienie poprzedza, następuje po nim, czy występuje w tej samej pozycji w kolejności sortowania co inny obiekt.

Wraca -

Mniejsze niż zero - to wystąpienie poprzedza obj w kolejności sortowania. Zero - to wystąpienie występuje w tej samej pozycji w kolejności sortowania co obj. Większe niż zero - to wystąpienie następuje po obj w kolejności sortowania.

Może zgłosić ArgumentException, jeśli obiekt nie jest tego samego typu co wystąpienie.

Na przykład możesz odwiedzić tutaj.

Dlatego sugeruję, aby lepiej użyć Equals zamiast compareTo.


Nie ma GetHashCode()metody. Czy masz na myśli hashCode()?
Markiz Lorne

0

„równa się” porównuje obiekty i zwraca prawdę lub fałsz, a „porównaj z” zwraca 0, jeśli jest prawdą lub liczbą [> 0] lub [<0], jeśli jest fałszem. Oto przykład:

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

Wyniki:

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

Dokumentacja Porównaj z: https://docs.oracle.com/javase/7/docs/api/java/lang/Comparable.html

Dokumentacja równa się: https://docs.oracle.com/javase/7/docs/api/java/lang/Object.html#equals(java.lang.Object)


0

Tutaj jedna rzecz jest ważna podczas używania compareTo()over, equals()który compareTodziała dla klas, które implementują interfejs `` Porównywalny '', w przeciwnym razie wyrzuci plik NullPointerException. Stringklasy implementują porównywalny interfejs, podczas gdy StringBuffernie można go używać "foo".compareTo("doo")w Stringobiekcie, ale nie w StringBufferObject.


0
String s1 = "a";
String s2 = "c";

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

To wypisuje -2 i false

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

Wypisuje 2 i false

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

To wypisuje 0 i prawda

equals zwraca wartość logiczną wtedy i tylko wtedy, gdy oba ciągi są zgodne.

Funkcja compareTo ma nie tylko powiedzieć, czy pasują, ale także powiedzieć, który ciąg jest mniejszy od drugiego, a także o ile, leksykograficznie. Jest to używane głównie podczas sortowania w kolekcji.


-1

Uważam equalsi equalsIgnoreCasemetody Stringzwrotu truei falseco jest przydatne, jeśli chcesz porównać wartości obiektu typu string, ale w przypadku implementacji compareToi compareToIgnoreCasemetod zwraca wartość dodatnią, ujemną i zerową, co będzie przydatne w przypadku sortowania.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.