CompareTo () so với equals ()


118

Khi kiểm tra tính bình đẳng của Stringtrong Java, tôi luôn sử dụng equals()vì đối với tôi, đây dường như là phương pháp tự nhiên nhất cho nó. Rốt cuộc, tên của nó đã nói lên những gì nó dự định làm. Tuy nhiên, một đồng nghiệp của tôi gần đây nói với tôi đã được dạy để sử dụng compareTo() == 0thay thế equals(). Điều này cảm thấy không tự nhiên (vì compareTo()nó được sử dụng để cung cấp một trật tự chứ không phải so sánh để bình đẳng) và thậm chí hơi nguy hiểm (vì compareTo() == 0không nhất thiết bao hàm sự bình đẳng trong mọi trường hợp, mặc dù tôi biết điều đó Stringlà đúng) đối với tôi.

Anh ấy không biết tại sao anh ấy được dạy sử dụng compareTo()thay vì equals()for String, và tôi cũng không thể tìm ra lý do tại sao. Đây thực sự là một vấn đề của sở thích cá nhân, hay có bất kỳ lý do thực sự nào cho một trong hai phương pháp?


9
Nói chính xác ở mức độ tối ưu hóa vi mô, điều mà chúng ta không bao giờ nên nói sớm, .equalsIgnoreCase()là so sánh nhanh nhất nếu nó phù hợp, nếu không thì đó .equals()là điều bạn muốn.

1
Đây là một câu hỏi cũ, nhưng nó chứa đựng một vấn đề mà tôi muốn nhấn mạnh: " compareTo() == 0không nhất thiết ngụ ý sự bình đẳng trong mọi trường hợp". Điều này hoàn toàn chính xác! Đây chính xác là những gì cụm từ "nhất quán với bằng" có nghĩa là trong các đặc tả API của Java, ví dụ như trong đặc tả lớp So sánh . Ví dụ: phương pháp so sánh của String phù hợp với bằng, nhưng phương pháp so sánh của BigDecimal không phù hợp với bằng.
Stuart Marks

Câu trả lời:


104

Một sự khác biệt là "foo".equals((String)null)trả về false trong khi "foo".compareTo((String)null) == 0ném NullPointerException. Vì vậy, chúng không phải lúc nào cũng có thể hoán đổi cho nhau ngay cả đối với Chuỗi.


6
Tôi nghĩ bạn cũng nên đề cập đến điều này. bằng tính toán mã băm. Mã băm của hai chuỗi khác nhau, mặc dù hiếm nhưng có thể giống nhau. Nhưng khi sử dụng CompareTo (), nó sẽ kiểm tra chuỗi ký tự từng ký tự. Vì vậy, trong trường hợp này, hai chuỗi sẽ trả về true nếu và chỉ khi nội dung thực của chúng bằng nhau.
Ashwin

30
Tại sao bạn nghĩ rằng bằng tính toán mã băm? Bạn có thể thấy rằng đây không phải là trường hợp: docjar.com/html/api/java/lang/String.java.html (dòng 1013).
waxwing

3
bạn đúng rồi. Tôi nghĩ rằng bằng và mã băm đi đôi với nhau (kiểm tra bằng nhau xem cả hai có cùng mã băm hay không). Vậy tại sao cần ghi đè cả hai phương thức này nếu bạn quyết định ghi đè bất kỳ phương thức nào trong số chúng.
Ashwin

3
Nó là cần thiết để ghi đè hashcode khi chúng ta ghi đè lên bằng bởi vì nếu lớp sử dụng bộ sưu tập băm dựa trên, bao gồm HashMap, HashSet, và Hashtable chức năng lớp sẽ không đúng cách
varunthacker

4
@Ashwin Điều này là cần thiết bởi vì nếu .equals trả về true, thì các mã băm cũng phải bằng nhau. Tuy nhiên, nếu mã băm bằng nhau thì điều đó không có nghĩa là .equals sẽ trả về true
Alan

33

2 điểm khác biệt chính là:

  1. equalssẽ lấy bất kỳ Đối tượng nào làm tham số, nhưng compareTosẽ chỉ lấy Chuỗi.
  2. equalschỉ cho bạn biết liệu chúng có bằng nhau hay không, nhưng compareTocung cấp thông tin về cách các Chuỗi so sánh về mặt từ vựng.

Tôi đã xem xét mã lớp String và thuật toán trong CompareTo và equals về cơ bản giống nhau. Tôi tin rằng ý kiến ​​của anh ấy chỉ là vấn đề sở thích, và tôi đồng ý với bạn - nếu tất cả những gì bạn cần biết là sự bình đẳng của các Chuỗi chứ không phải cái nào xuất hiện trước về mặt từ vựng, thì tôi sẽ sử dụng equals.


26

Khi so sánh để bình đẳng, bạn nên sử dụng equals(), vì nó thể hiện ý định của bạn một cách rõ ràng.

compareTo()có một nhược điểm bổ sung là nó chỉ hoạt động trên các đối tượng triển khai Comparablegiao diện.

Điều này áp dụng chung, không chỉ cho Chuỗi.


18

compareTophải làm nhiều việc hơn nếu các chuỗi có độ dài khác nhau. equalschỉ có thể trả về false, trong khi compareToluôn phải kiểm tra đủ các ký tự để tìm thứ tự sắp xếp.


10

compareTo()không chỉ áp dụng cho Chuỗi mà còn cho bất kỳ đối tượng nào khác vì compareTo<T>có một đối số chung T. String là một trong những lớp đã triển khai compareTo()phương thức bằng cách triển khai Comparablegiao diện. (CompareTo () là một phương thức cho Giao diện có thể so sánh được). Vì vậy, bất kỳ lớp nào cũng được miễn phí để triển khai giao diện So sánh.

Nhưng compareTo()đưa ra thứ tự của các đối tượng , thường được sử dụng để sắp xếp các đối tượng theo thứ tự tăng dần hoặc giảm dần trong khi equals()sẽ chỉ nói về sự bằng nhau và cho biết liệu chúng có bằng nhau hay không.


10

Trong ngữ cảnh chuỗi
: so sánhTo: So sánh hai chuỗi về mặt từ vựng.
bằng: So sánh chuỗi này với đối tượng được chỉ định.

So sánh hai chuỗi theo ký tự của chúng (tại cùng một chỉ mục) và trả về một số nguyên (dương hoặc âm) tương ứng.

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 () có thể hiệu quả hơn thì so sánhTo () .

Một sự khác biệt rất quan trọng giữa CompareTo và equals:

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

equals () kiểm tra xem hai đối tượng có giống nhau hay không và trả về một boolean.

CompareTo () (từ giao diện có thể so sánh được) trả về một số nguyên. Nó kiểm tra đối tượng nào trong hai đối tượng là "nhỏ hơn", "bằng" hoặc "lớn hơn" đối tượng kia. Không phải tất cả các đối tượng đều có thể được sắp xếp theo thứ tự hợp lý, vì vậy phương thức CompareTo () không phải lúc nào cũng có ý nghĩa.

Lưu ý rằng equals () không xác định thứ tự giữa các đối tượng, so sánh thì có.

Bây giờ tôi khuyên bạn nên xem lại mã nguồn của cả hai phương pháp để kết luận rằng equals thích hợp hơn so với CompareTo liên quan đến một số phép tính Toán học.


5

Có vẻ như cả hai phương thức đều làm được điều tương tự, nhưng phương thức CompareTo () nhận trong một Chuỗi chứ không phải một Đối tượng và thêm một số chức năng bổ sung trên đầu phương thức equals () bình thường. Nếu tất cả những gì bạn quan tâm là bình đẳng, thì phương thức equals () là lựa chọn tốt nhất, đơn giản vì nó có ý nghĩa hơn đối với lập trình viên tiếp theo xem xét mã của bạn. Chênh lệch thời gian giữa hai chức năng khác nhau sẽ không thành vấn đề trừ khi bạn đang lặp lại một số lượng lớn các mục. CompareTo () thực sự hữu ích khi bạn cần biết thứ tự của các chuỗi trong một tập hợp hoặc khi bạn cần biết sự khác biệt về độ dài giữa các chuỗi bắt đầu với cùng một chuỗi ký tự.

nguồn: http://java.sun.com/javase/6/docs/api/java/lang/String.html


5

equals() nên là phương pháp được lựa chọn trong trường hợp OP.

Nhìn vào việc triển khai equals()compareTo()trong java.lang.String trên grepcode , chúng ta có thể dễ dàng thấy rằng bằng sẽ tốt hơn nếu chúng ta chỉ quan tâm đến sự bằng nhau của hai chuỗi:

equals():

1012   public  boolean bằng ( Object anObject) { 
1013 if ( this == anObject) {
1014 return true ;
1015 }
1016 if (anObject instanceof String ) {
1017 String anotherString = ( String ) anObject;
1018 int n = số đếm;
1019 if (n == anotherString.count) {
1020 char v1 [] = value;
1021 char v2 [] = anotherString.value;
1022 inti = bù đắp;
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 }

compareTo():

1174   public  int so sánhTo ( String anotherString) { 
1175 int len1 = count;
1176 int len2 = anotherString.count;
1177 int n = Toán học. min (len1, len2);
1178 char v1 [] = giá trị;
1179 char v2 [] = anotherString.value;
1180 int i = bù đắp;
1181 int j = anotherString.offset;
1183 if (i == j) {
1184 int k = i;
1185 int lim = n + i;
1186 trong khi (k <lim) {
1187 char 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 char c2 = v2 [j ++];
1198 if (c1! = C2) {
1199 return c1 - c2;
1200 }
1201 }
1202 }
1203 return len1 - len2;
1204 }

Khi một trong các chuỗi là tiền tố của chuỗi khác, hiệu suất của compareTo()sẽ kém hơn vì nó vẫn cần xác định thứ tự từ vựng trong khi equals()sẽ không lo lắng nữa và trả về false ngay lập tức.

Theo ý kiến ​​của tôi, chúng ta nên sử dụng hai cái này như mục đích của chúng:

  • equals() để kiểm tra sự bình đẳng, và
  • compareTo() để tìm thứ tự từ vựng.

3

equals () kiểm tra xem hai chuỗi có bằng nhau hay không, nó cho giá trị boolean. CompareTo () kiểm tra xem đối tượng chuỗi có bằng, lớn hơn hay nhỏ hơn đối với đối tượng chuỗi khác hay không. Kết quả là: 1 nếu đối tượng chuỗi lớn hơn 0 nếu cả hai bằng -1 nếu chuỗi nhỏ hơn chuỗi khác

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

Đây là một thử nghiệm trong chiêu hồn :-)

Hầu hết các câu trả lời đều so sánh hiệu suất và sự khác biệt của API. Họ bỏ lỡ điểm cơ bản rằng hai hoạt động chỉ đơn giản là có ngữ nghĩa khác nhau.

Trực giác của bạn là đúng. x.equals (y) không thể hoán đổi với x.compareTo (y) == 0. Cái đầu tiên so sánh danh tính, trong khi cái kia so sánh khái niệm 'kích thước'. Đúng là trong nhiều trường hợp, đặc biệt là với các kiểu nguyên thủy, hai thứ này đồng chỉnh với nhau.

Trường hợp chung là:

Nếu x và y giống hệt nhau, chúng có cùng 'kích thước': nếu x.equals (y) là true => x.compareTo (y) là 0.

Tuy nhiên, nếu x và y có cùng kích thước thì không có nghĩa là chúng giống hệt nhau.

nếu x.compareTo (y) là 0 không nhất thiết có nghĩa là x.equals (y) là đúng.

Một ví dụ hấp dẫn trong đó danh tính khác với kích thước sẽ là các số phức. Giả sử rằng việc so sánh được thực hiện bằng giá trị tuyệt đối của chúng. Vậy cho trước hai số phức: Z1 = a1 + b1 * i và Z2 = a2 + b2 * i:

Z1.equals (z2) trả về true nếu và chỉ khi a1 = a2 và b1 = b2.

Tuy nhiên Z1.compareTo (Z2) trả về 0 cho và vô số cặp (a1, b1) và (a2, b2) miễn là chúng thỏa mãn điều kiện a1 ^ 2 + b1 ^ 2 == a2 ^ 2 + b2 ^ 2.


1
x.equals (y) không có nghĩa là Identity, nó có nghĩa là Bình đẳng. Danh tính được so sánh bằng cách sử dụng x == y cho các kiểu do người dùng xác định trong Java.
Fernando Pelliccioni

2

Bằng có thể hiệu quả hơn sau đó so sánhTo.

Nếu độ dài của các chuỗi ký tự trong Chuỗi không khớp thì không có cách nào mà các Chuỗi bằng nhau để việc từ chối có thể nhanh hơn nhiều.

Hơn nữa, nếu nó là cùng một đối tượng (bình đẳng danh tính chứ không phải bình đẳng logic), nó cũng sẽ hiệu quả hơn.

Nếu họ cũng triển khai hashCode caching thì có thể nhanh hơn nữa để từ chối các non-bằng trong trường hợp hashCode của họ không khớp.


2

String.equals()yêu cầu gọi instanceoftoán tử trong khi compareTo()yêu cầu không. Đồng nghiệp của tôi đã ghi nhận sự sụt giảm hiệu suất lớn gây ra bởi quá nhiều instanceofcuộc gọi trong equals()phương thức, tuy nhiên, thử nghiệm của tôi đã được chứng minh compareTo()là chỉ nhanh hơn một chút.

Tuy nhiên, tôi đang sử dụng Java 1.6. Trên các phiên bản khác (hoặc các nhà cung cấp JDK khác), sự khác biệt có thể lớn hơn.

Thử nghiệm so sánh từng chuỗi trong 1000 mảng phần tử, lặp lại 10 lần.


1
Tôi đoán bạn phải sử dụng các chuỗi rất ngắn, tất cả đều có cùng độ dài và có độ đa dạng cao ở ký tự đầu tiên để nhận được kết quả compareTo()nhanh hơn ở equals()đâu? Nhưng đối với hầu hết các bộ dữ liệu thế giới thực equals()thì nhanh hơn nhiều compareTo() == 0. equals()chiếu sáng nếu các chuỗi có tiền tố chung nhưng độ dài khác nhau hoặc nếu chúng thực sự là cùng một đối tượng.
x4u

Chà, thử nghiệm của tôi đã chống lại giả thuyết rằng ví dụ của nó là kẻ giết chết hiệu suất, vì vậy các chuỗi thực sự ngắn.
Danubian Sailor

Nếu chuỗi của bạn là ngẫu nhiên và hầu hết là khác nhau về độ dài thì lựa chọn rõ ràng hơn nên là phương thức equals () thay vì so sánhTo () vì trong hầu hết các trường hợp, nó sẽ ngay lập tức trả về false nếu chúng không có độ dài bằng nhau.
sactiw

1
  1. equalscó thể lấy bất kỳ Đối tượng nào làm tham số nhưng compareTochỉ có thể lấy Chuỗi.

  2. khi cometo null, compareTosẽ ném ra một ngoại lệ

  3. khi bạn muốn biết nơi khác biệt xảy ra, bạn có thể sử dụng compareTo.


CompareTo có thể lấy bất kỳ đối tượng nào làm đối số. Như @apurva jadhav đã nói ở trên, so sánh có một đối số chung. Nếu bạn triển khai có thể so sánh được dưới dạng <Chuỗi> có thể so sánh được, thì bạn chỉ được phép sử dụng các chuỗi.
Gaurav Kumar

1
  • equals: cần thiết để kiểm tra tính bình đẳng và hạn chế trùng lặp. Nhiều lớp của Thư viện Java sử dụng điều này trong trường hợp họ muốn tìm các bản sao. Ví dụ: HashSet.add(ob1)sẽ chỉ thêm nếu điều đó không tồn tại. Vì vậy, nếu bạn đang mở rộng một số lớp như thế này thì hãy ghi đè equals().

  • compareTo: yêu cầu cho thứ tự của phần tử. Một lần nữa để sắp xếp ổn định, bạn yêu cầu bình đẳng, vì vậy sẽ có giá trị trả về 0.


0

Bằng -

1- Ghi đè phương thức GetHashCode để cho phép một kiểu hoạt động chính xác trong bảng băm.

2- Không ném một ngoại lệ trong việc thực hiện một phương thức Equals. Thay vào đó, trả về false cho một đối số null.

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.

Các lệnh gọi liên tiếp của x.Equals (y) trả về cùng một giá trị miễn là đối tượng được tham chiếu bởi x và y không bị sửa đổi.

x.Equals(null) returns false.

4- Đối với một số loại đối tượng, cần kiểm tra Equals cho giá trị bình đẳng thay vì bình đẳng tham chiếu. Việc triển khai Equals như vậy trả về true nếu hai đối tượng có cùng giá trị, ngay cả khi chúng không phải là cùng một thể hiện.

Ví dụ -

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

Đầu ra: -

False
True

trong khi sánhTo -

So sánh cá thể hiện tại với một đối tượng khác cùng loại và trả về một số nguyên cho biết liệu cá thể hiện tại đứng trước, theo sau hay xuất hiện ở cùng một vị trí theo thứ tự sắp xếp như đối tượng kia.

Nó trở lại -

Nhỏ hơn 0 - Trường hợp này đứng trước obj theo thứ tự sắp xếp. Zero - Trường hợp này xảy ra ở cùng một vị trí theo thứ tự sắp xếp như obj. Lớn hơn 0 - Trường hợp này theo sau obj theo thứ tự sắp xếp.

Nó có thể ném ArgumentException nếu đối tượng không cùng kiểu với thể hiện.

Ví dụ, bạn có thể truy cập vào đây.

Vì vậy, tôi khuyên bạn nên sử dụng Equals thay cho CompareTo.


Không có GetHashCode()phương pháp. Ý bạn là hashCode()?
Marquis of Lorne

0

"bằng" so sánh các đối tượng và trả về true hoặc false và "so sánh với" trả về 0 nếu là đúng hoặc một số [> 0] hoặc [<0] nếu là sai, đây là một ví dụ:

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

Các kết quả:

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

Tài liệu So sánh với: https://docs.oracle.com/javase/7/docs/api/java/lang/Comporing.html

Tài liệu Equals: https://docs.oracle.com/javase/7/docs/api/java/lang/Object.html#equals(java.lang.Object)


0

Đây là một điều quan trọng trong khi sử dụng compareTo()over equals()đó compareTohoạt động đối với các lớp triển khai giao diện 'So sánh' nếu không nó sẽ ném ra một NullPointerException. Stringcác lớp thực hiện Giao diện có thể so sánh trong khi StringBufferkhông vì vậy bạn có thể sử dụng "foo".compareTo("doo")trong Stringđối tượng nhưng không sử dụng trong StringBufferĐối tượng.


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

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

Điều này in -2 và sai

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

Điều này in 2 và sai

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

Điều này in ra 0 và true

bằng trả về boolean nếu và chỉ khi cả hai chuỗi đều khớp.

CompareTo có nghĩa là không chỉ cho biết liệu chúng có khớp hay không mà còn cho biết Chuỗi nào nhỏ hơn chuỗi kia, và còn bao nhiêu, về mặt từ vựng. Điều này chủ yếu được sử dụng trong khi phân loại trong bộ sưu tập.


-1

Tôi tin equalsequalsIgnoreCasephương pháp Stringtrở lại truefalseđó là hữu ích nếu bạn muốn so sánh giá trị của đối tượng chuỗi, Nhưng trong trường hợp thực hiện compareTocompareToIgnoreCasephương pháp lợi nhuận tích cực, tiêu cực và zero giá trị đó sẽ hữu ích trong trường hợp phân loại.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.