порівняти до () проти рівних ()


118

При тестуванні на рівність String's у Java я завжди використовував, equals()тому що мені це здається найбільш природним методом. Зрештою, його назва вже говорить про те, що він призначений робити. Однак нещодавно моя колега сказала, що мене навчили використовувати compareTo() == 0замість цього equals(). Це відчувається неприродно (як compareTo()мається на увазі впорядкувати, а не порівнювати рівність) і навіть дещо небезпечно (бо compareTo() == 0не обов'язково означає рівність у всіх випадках, хоча я знаю, що це робить для Stringмене).

Він не знав , чому він вчив використовувати compareTo()замість equals()для String«s, і я також не міг знайти причину. Це справді питання особистого смаку, чи є якась реальна причина для будь-якого методу?


9
Суворо кажучи, на рівні мікрооптимізації, про який ми ніколи не повинні передчасно говорити, .equalsIgnoreCase()це найшвидше порівняння, якщо це доречно, інакше .equals()- те, що ви хочете.

1
Це давнє запитання, але воно містить самородок, який я хотів виділити: " compareTo() == 0не обов'язково означає рівність у всіх випадках". Це абсолютно правильно! Це саме те, що словосполучення "узгоджується з рівними" означає у специфікаціях Java API, наприклад, у специфікаціях класу Comparable . Наприклад, метод порівняння String відповідає рівним, але метод порівняння BigDecimal не відповідає рівним.
Стюарт відзначає

Відповіді:


104

Різниця полягає в тому, що "foo".equals((String)null)повертає помилкові під "foo".compareTo((String)null) == 0час передачі NullPointerException. Тому вони не завжди взаємозамінні навіть для струнних.


6
Я думаю, ви також повинні згадати про це. дорівнює обчислюванню хеш-коду. Хеш-код двох різних рядків, хоча і рідкісний, може бути однаковим. Але при використанні CompareTo () він перевіряє рядки на символ. Тож у цьому випадку два рядки повернуться істинними тоді і лише тоді, коли їх фактичний вміст рівний.
Ешвін

30
Чому ви вважаєте, що дорівнює обчислюванню хеш-коду? Ви можете бачити, що це не так: docjar.com/html/api/java/lang/String.java.html (рядок 1013).
обробка воском

3
ти правий. Я думав, що дорівнює і хеш-код іде рука об руку (дорівнює перевіряє, чи мають обоє однаковий хеш-код). Тоді чому потрібно переосмислити обидва ці способи, якщо ви вирішили перекрити будь-який із них.
Ешвін

3
Необхідно переосмислити хеш-код, коли ми переосмислимо рівняння, тому що якщо клас використовує колекції на основі хешу, включаючи HashMap, HashSet і Hashtable, клас звичайно функціонує нормально
varunthacker

4
@Ashwin Це необхідно, тому що, якщо .equals повертає true, хеш-коди також повинні бути рівними. Однак якщо хеш-коди рівні, це не означає, що еквіваленти повинні повернути справжнє
Алан

33

Дві основні відмінності полягають у тому, що:

  1. equalsвізьме будь-який об’єкт як параметр, але compareToвізьме лише рядки.
  2. equalsлише розповідає, чи вони рівні чи ні, але compareToдає інформацію про те, як лексикографічно порівнюються рядки.

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


26

Якщо порівнювати рівність, вам слід скористатися equals(), оскільки це чітко виражає ваші наміри.

compareTo()має додатковий недолік, що він працює лише на об'єктах, що реалізують Comparableінтерфейс.

Це стосується загалом, не тільки для Strings.


18

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


10

compareTo()застосовується не лише до Strings, але й до будь-якого іншого об'єкта, тому що 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

7

рівним () може бути більш ефективним, ніж порівняти до () .

Дуже важлива різниця між порівнянням та рівним:

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

equals () перевіряє, чи два об'єкти однакові чи ні, і повертає булева.

CompareTo () (від інтерфейсу Comparable) повертає ціле число. Він перевіряє, який із двох об’єктів "менший за", "рівний" або "більший за" інший. Не всі об'єкти можуть бути впорядковані логічно, тому метод CompareTo () не завжди має сенс.

Зауважте, що рівняння () не визначає впорядкування між об'єктами, яке порівнюєTo ().

Тепер я раджу переглянути вихідний код обох методів, щоб зробити висновок, що дорівнює перевагу порівняно з порівнянням.


5

Здається, що обидва методи в основному роблять одне і те ж, але метод сравнениеTo () бере в собі String, а не Об'єкт, і додає додаткову функціональність поверх методу normal equals (). Якщо все, що вам цікаво, це рівність, то метод equals () - найкращий вибір, просто тому, що це має більше сенсу для наступного програміста, який придивиться до вашого коду. Різниця в часі між двома різними функціями не має значення, якщо ви не перебираєте певну кількість предметів. CompareTo () дійсно корисний, коли вам потрібно знати порядок рядків у колекції або коли вам потрібно знати різницю в довжині між рядками, що починаються з тієї ж послідовності символів.

джерело: http://java.sun.com/javase/6/docs/api/java/lang/String.html


5

equals() повинен бути методом вибору у випадку з ОП.

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

equals():

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

і compareTo():

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

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

На мою думку, ми повинні використовувати ці два як вони були призначені:

  • 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

Існують певні речі, про які потрібно пам’ятати, переосмислюючи сравнениеTo на Java, наприклад, Порівняння має відповідати рівним, і віднімання не повинно використовуватися для порівняння цілих полів, оскільки вони можуть переповнюватись. Перевірте речі, які слід пам’ятати, перебираючи компаратор на 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

Рівні можуть бути ефективнішими порівняно зTo.

Якщо довжина символьних послідовностей у String не збігається, то струни рівні, тому відхилення може бути набагато швидшим.

Більше того, якщо це той самий об’єкт (рівність ідентичності, а не логічна рівність), він буде також більш ефективним.

Якщо вони також застосували кешування hashCode, може бути ще швидше відхилити нерівні, якщо їх хеш-код не збігається.


2

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

Я використовував, однак, Java 1.6. В інших версіях (або інших постачальниках JDK) різниця може бути більшою.

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


1
Я думаю, ви, мабуть, використовували дуже короткі рядки, однакові за довжиною і з великою різноманітністю на першому символі, щоб отримати результат, де compareTo()швидше, ніж equals()? Але для більшості наборів реальних даних дані equals()набагато швидше, ніж compareTo() == 0. equals()світить, якщо рядки мають загальний префікс, але різної довжини або якщо вони насправді є одним і тим же об'єктом.
x4u

Що ж, мій тест був проти гіпотезу, що instanceof є вбивцею продуктивності, тому рядки справді були короткими.
Дунайський матрос

Якщо ваш рядок є випадковим і здебільшого відрізняється по довжині, тоді більш очевидним вибором повинен бути метод рівний () над сравнениеTo (), оскільки в більшості випадків він негайно поверне помилковий, якщо вони не рівні за довжиною.
sactiw

1
  1. equalsможе приймати будь-який об’єкт як параметр, але compareToможе приймати лише String.

  2. коли cometo null, compareToвикине виняток

  3. коли ви хочете знати, де відбувається різниця, ви можете використовувати compareTo.


CompareTo може взяти будь-який об'єкт як аргумент. Як сказав @apurva jadhav вище, порівняння бере загальний аргумент. Якщо ви реалізуєте порівняний як Порівнюючий <String>, то вам дозволяється використовувати лише рядки.
Гаурав Кумар

1
  • equals: потрібно для перевірки рівності та обмеження дублікатів. Багато класів бібліотеки Java використовують це у випадку, якщо вони хотіли знайти дублікати. наприклад, HashSet.add(ob1)буде додано лише в тому випадку, якщо цього не існує. Тож якщо ви продовжуєте такі класи, то перезазначте equals().

  • compareTo: необхідна для замовлення елемента. Знову для стабільного сортування вам потрібна рівність, тому є повернення 0.


0

Дорівнює -

1- Замініть метод GetHashCode, щоб дозволити типу працювати коректно в хеш-таблиці.

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

Наприклад -

   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, якщо об'єкт не того самого типу, як екземпляр.

Наприклад, ви можете відвідати тут.

Тому я пропоную краще використовувати рівний замість порівняння.


Немає 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()над тим, equals()що compareToпрацює для класів, які реалізують інтерфейс "Порівняний", інакше він буде кидати NullPointerException. StringКласи реалізує Порівнюваний інтерфейс, StringBufferтому не можна використовувати його "foo".compareTo("doo")в Stringоб'єкті, але не в StringBufferObject.


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 і правда

дорівнює, повертається булевим, якщо і лише якщо обидва рядки збігаються.

сравнениеTo має на увазі не просто сказати, чи вони відповідають, але й сказати, яка струна менша за іншу, а також на скільки, лексикографічно. В основному це використовується під час сортування у колекції.


-1

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

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