JavaRush /Blog Java /Random-VI /Bằng trong so sánh Java và Chuỗi - So sánh chuỗi

Bằng trong so sánh Java và Chuỗi - So sánh chuỗi

Xuất bản trong nhóm
Xin chào! Hôm nay chúng ta sẽ nói về một chủ đề rất quan trọng và thú vị, đó là so sánh các đối tượng với nhau bằng() trong Java. Và thực sự, trong trường hợp nào trong Java Object A sẽ bằng Object B ? So sánh bằng và chuỗi - 1Hãy thử viết một ví dụ:
public class Car {

   String model;
   int maxSpeed;

   public static void main(String[] args) {

       Car car1 = new Car();
       car1.model = "Ferrari";
       car1.maxSpeed = 300;

       Car car2 = new Car();
       car2.model = "Ferrari";
       car2.maxSpeed = 300;

       System.out.println(car1 == car2);
   }
}
Đầu ra của bảng điều khiển:

false
Được rồi, dừng lại. Thực tế tại sao hai chiếc xe này lại không bằng nhau? Chúng tôi đã cho chúng những đặc tính giống nhau, nhưng kết quả so sánh là sai. Đáp án đơn giản. Toán tử ==không so sánh các thuộc tính của đối tượng mà so sánh các liên kết. Ngay cả khi hai đối tượng có 500 thuộc tính giống nhau thì kết quả so sánh vẫn sai. Rốt cuộc, các liên kết car1trỏ car2 đến hai đối tượng khác nhau , đến hai địa chỉ khác nhau. Hãy tưởng tượng một tình huống so sánh mọi người. Chắc chắn trên thế giới này sẽ có một người có cùng tên, màu mắt, tuổi, chiều cao, màu tóc, v.v. với bạn. Nghĩa là, các bạn giống nhau về nhiều mặt, nhưng các bạn vẫn không phải là anh em sinh đôi, và đặc biệt là không phải cùng một người. So sánh bằng và chuỗi - 2Toán tử áp dụng logic gần giống nhau ==khi chúng ta sử dụng nó để so sánh hai đối tượng. Nhưng nếu bạn cần logic khác trong chương trình của mình thì sao? Ví dụ: nếu chương trình của bạn mô phỏng phân tích DNA. Cô phải so sánh mã DNA của hai người và xác định họ là anh em sinh đôi.
public class Man {

   int dnaCode;

   public static void main(String[] args) {

       Man man1 = new Man();
       man1.dnaCode = 1111222233;

       Man man2 = new Man();
       man2.dnaCode = 1111222233;

       System.out.println(man1 == man2);
   }
}
Đầu ra của bảng điều khiển:

false
Thật hợp lý khi kết quả vẫn như vậy (xét cho cùng, chúng tôi không thay đổi gì cả), nhưng giờ chúng tôi không hài lòng với nó! Quả thực, trong đời thực, phân tích DNA đảm bảo một trăm phần trăm rằng chúng ta đang đối mặt với những cặp song sinh. Nhưng chương trình và nhà điều hành của chúng tôi ==lại cho chúng tôi biết điều ngược lại. Làm cách nào chúng ta có thể thay đổi hành vi này và đảm bảo rằng nếu kết quả xét nghiệm ADN trùng khớp thì chương trình sẽ đưa ra kết quả chính xác? Với mục đích này, một phương thức đặc biệt đã được tạo trong Java - Equals() .

Phương thức Equals() trong Java

Giống như phương thức toString()mà chúng ta đã thảo luận trước đó, Equals() thuộc về lớp, Objectlớp quan trọng nhất trong Java, mà tất cả các lớp khác đều bắt nguồn từ đó. Tuy nhiên, bản thân Equals() sẽ không thay đổi hành vi của chương trình theo bất kỳ cách nào:
public class Man {

   String dnaCode;

   public static void main(String[] args) {

       Man man1 = new Man();
       man1.dnaCode = "111122223333";

       Man man2 = new Man();
       man2.dnaCode = "111122223333";

       System.out.println(man1.equals(man2));
   }
}
Đầu ra của bảng điều khiển:

false
Kết quả hoàn toàn giống nhau, vậy tại sao lại cần phương pháp này? :/ Thật đơn giản. Thực tế là bây giờ chúng tôi đã sử dụng phương pháp này vì nó được triển khai trong chính lớp đó Object. Và nếu chúng ta đi vào mã lớp Objectvà xem phương thức này được triển khai như thế nào và nó làm gì, chúng ta sẽ thấy:
public boolean equals(Object obj) {
   return (this == obj);
}
Đây là lý do tại sao hành vi của chương trình của chúng tôi không thay đổi! Bên trong phương thức Equals() của lớp Objectcó cùng một so sánh tham chiếu tương tự, ==. Nhưng mẹo của phương pháp này là chúng ta có thể ghi đè lên nó. Ghi đè có nghĩa là viết phương thức Equals() của riêng bạn trong lớp của chúng ta Manvà làm cho nó hoạt động theo cách chúng ta muốn! Bây giờ chúng tôi không hài lòng rằng man1.equals(man2)về cơ bản, việc kiểm tra cũng thực hiện giống như man1 == man2. Đây là những gì chúng ta sẽ làm trong tình huống này:
public class Man {

   int dnaCode;

   public boolean equals(Man man) {
       return this.dnaCode ==  man.dnaCode;
   }

   public static void main(String[] args) {

       Man man1 = new Man();
       man1.dnaCode = 1111222233;

       Man man2 = new Man();
       man2.dnaCode = 1111222233;

       System.out.println(man1.equals(man2));

   }
}
Đầu ra của bảng điều khiển:

true
Một kết quả hoàn toàn khác! Bằng cách viết phương thức Equals() của riêng mình thay vì phương thức tiêu chuẩn, chúng ta đã đạt được hành vi đúng: bây giờ nếu hai người có cùng mã DNA, chương trình sẽ cho chúng ta biết: “Phân tích DNA cho thấy họ là anh em sinh đôi” và trả về giá trị true! Bằng cách ghi đè phương thức bằng() trong các lớp của mình, bạn có thể dễ dàng tạo logic so sánh đối tượng cần thiết. Chúng tôi chỉ đề cập đến việc so sánh các đối tượng một cách chung chung. Chúng tôi vẫn sẽ có một bài giảng lớn riêng về chủ đề này ở phía trước (bạn có thể đọc nhanh ngay bây giờ nếu quan tâm).

So sánh chuỗi trong Java - So sánh chuỗi

Tại sao chúng ta xử lý việc so sánh chuỗi một cách riêng biệt với mọi thứ khác? Thực ra, các dòng trong lập trình là một câu chuyện hoàn toàn khác. Thứ nhất, nếu bạn xem xét tất cả các chương trình Java do loài người viết ra, khoảng 25% đối tượng trong đó được tạo thành từ chúng. Vì vậy, chủ đề này là rất quan trọng. Thứ hai, quá trình so sánh các chuỗi thực sự khá khác biệt so với các đối tượng khác. Hãy xem một ví dụ đơn giản:
public class Main {

   public static void main(String[] args) {

       String s1 = "JavaRush is the best site to learn Java!";
       String s2 = new String("JavaRush is the best site to learn Java!");
       System.out.println(s1 == s2);
   }
}
Đầu ra của bảng điều khiển:

false
Nhưng tại sao lại sai? Các dòng giống hệt nhau, từng chữ một :/ Bạn có thể cho rằng: điều này là do toán tử == so sánh các tham chiếu! Rốt cuộc, s1họ s2có địa chỉ khác nhau trong bộ nhớ. Nếu ý nghĩ này xảy ra với bạn, thì hãy làm lại ví dụ của chúng ta:
public class Main {

   public static void main(String[] args) {

       String s1 = "JavaRush is the best site to learn Java!";
       String s2 = "JavaRush is the best site to learn Java!";
       System.out.println(s1 == s2);
   }
}
Bây giờ chúng ta cũng có 2 link nhưng kết quả lại ngược lại:

true
Hoàn toàn bối rối? :) Hãy tìm ra nó. Toán tử ==thực sự so sánh các địa chỉ trong bộ nhớ. Quy tắc này luôn hoạt động và không cần phải nghi ngờ gì về nó. Điều này có nghĩa là nếu s1 == s2trả về true thì hai chuỗi này có cùng địa chỉ trong bộ nhớ. Và thực sự là như vậy! Đã đến lúc làm quen với một vùng bộ nhớ đặc biệt để lưu trữ chuỗi - nhóm chuỗi ( String pool) So sánh bằng và chuỗi - 3Nhóm chuỗi là vùng lưu trữ tất cả các giá trị chuỗi mà bạn tạo trong chương trình của mình. Nó được tạo ra để làm gì? Như đã đề cập trước đó, chuỗi chiếm một phần rất lớn trong tất cả các đối tượng. Trong bất kỳ chương trình lớn nào, rất nhiều dòng được tạo ra. Để tiết kiệm bộ nhớ, đây là điều cần thiết String Pool- một dòng có văn bản bạn cần được đặt ở đó và trong tương lai, các liên kết mới được tạo sẽ đề cập đến cùng một vùng bộ nhớ, không cần phải phân bổ thêm bộ nhớ mỗi lần. Mỗi lần bạn viết String = “........”, chương trình sẽ kiểm tra xem có dòng nào có văn bản như vậy trong nhóm chuỗi hay không. Nếu có, một cái mới sẽ không được tạo ra. Và liên kết mới sẽ trỏ đến cùng một địa chỉ trong nhóm chuỗi nơi chuỗi này được lưu trữ. Vì vậy, khi viết chương trình
String s1 = "JavaRush is the best site to learn Java!";
String s2 = "JavaRush is the best site to learn Java!";
liên kết s2trỏ chính xác đến cùng một vị trí với s1. Lệnh đầu tiên đã tạo một dòng mới trong nhóm chuỗi với văn bản chúng ta cần và khi đến dòng thứ hai, nó chỉ đơn giản đề cập đến cùng một vùng bộ nhớ như s1. Bạn có thể viết thêm ít nhất 500 dòng với cùng một văn bản, kết quả sẽ không thay đổi. Dừng lại. Nhưng tại sao ví dụ này không có tác dụng với chúng ta trước đây?
public class Main {

   public static void main(String[] args) {

       String s1 = "JavaRush is the best site to learn Java!";
       String s2 = new String("JavaRush is the best site to learn Java!");
       System.out.println(s1 == s2);
   }
}
Tôi nghĩ, bằng trực giác, bạn đã đoán được lý do là gì :) Hãy thử đoán trước khi đọc tiếp. Bạn có thể thấy hai dòng này được tạo ra khác nhau. Một là với sự trợ giúp của người vận hành new, và thứ hai là không có nó. Đây chính xác là lý do. Toán tử mới khi tạo một đối tượng sẽ buộc phải cấp phát một vùng mới trong bộ nhớ cho đối tượng đó . Và dòng được tạo bằng new, không kết thúc bằng String Pool: nó trở thành một đối tượng riêng biệt, ngay cả khi văn bản của nó hoàn toàn giống với cùng một dòng từ String Pool'a. Đó là, nếu chúng ta viết đoạn mã sau:
public class Main {

   public static void main(String[] args) {

       String s1 = "JavaRush is the best site to learn Java!";
       String s2 = "JavaRush is the best site to learn Java!";
       String s3 = new String("JavaRush is the best site to learn Java!");
   }
}
Trong bộ nhớ, nó sẽ trông như thế này: So sánh bằng và chuỗi - 4Và mỗi khi một đối tượng mới được tạo, newmột vùng mới sẽ được cấp phát trong bộ nhớ, ngay cả khi văn bản bên trong các dòng mới giống nhau! Có vẻ như chúng ta đã sắp xếp xong toán tử ==, nhưng còn người bạn mới của chúng ta - phương thức Equals() thì sao?
public class Main {

   public static void main(String[] args) {

       String s1 = "JavaRush is the best site to learn Java!";
       String s2 = new String("JavaRush is the best site to learn Java!");
       System.out.println(s1.equals(s2));
   }
}
Đầu ra của bảng điều khiển:

true
Hấp dẫn. Chúng tôi biết chính xác những gì s1s2trỏ đến các khu vực khác nhau trong bộ nhớ. Tuy nhiên, phương thức bằng() nói rằng chúng bằng nhau. Tại sao? Bạn có nhớ ở trên chúng ta đã nói rằng phương thức Equals() có thể được ghi đè trong lớp của bạn để nó so sánh các đối tượng theo cách bạn cần không? Đó là những gì họ đã làm với lớp học String. Nó có một phương thức bằng() bị ghi đè. Và nó không so sánh các liên kết mà là chuỗi ký tự trong chuỗi. Và nếu văn bản trong các chuỗi giống nhau, thì chúng được tạo ra như thế nào và chúng được lưu trữ ở đâu không quan trọng: trong nhóm chuỗi hoặc trong một vùng bộ nhớ riêng. Kết quả so sánh sẽ đúng. Nhân tiện, Java cho phép bạn so sánh chính xác các chuỗi theo cách không phân biệt chữ hoa chữ thường. Trong tình huống bình thường, nếu bạn viết một trong các dòng, chẳng hạn như viết hoa, kết quả so sánh sẽ sai:
public class Main {

   public static void main(String[] args) {

       String s1 = "JavaRush is the best site to learn Java!";
       String s2 = new String("JAVARUSH - ЛУЧШИЙ САЙТ ДЛЯ ИЗУЧЕНИЯ JAVA!");
       System.out.println(s1.equals(s2));
   }
}
Đầu ra của bảng điều khiển:

false
Trong trường hợp này, lớp Stringcó một phương thức equalsIgnoreCase(). Nếu điều chính trong so sánh của bạn là chuỗi các ký tự cụ thể chứ không phải trường hợp của chúng, bạn có thể sử dụng nó. Ví dụ: điều này sẽ hữu ích khi so sánh hai địa chỉ email:
public class Main {

   public static void main(String[] args) {

       String address1 = "Moscow, Academician Korolev street, 12";
       String address2 = new String("Г. МОСКВА, УЛ. АКАДЕМИКА КОРОЛЕВА, ДОМ 12");
       System.out.println(address1.equalsIgnoreCase(address2));
   }
}
Trong trường hợp này, rõ ràng là chúng ta đang nói về cùng một địa chỉ nên việc sử dụng phương pháp này equalsIgnoreCase()sẽ là quyết định đúng đắn.

Phương thức String.intern()

Lớp này Stringcó một phương thức phức tạp khác - intern(); Phương thức này intern()hoạt động trực tiếp với String Pool'om. Nếu bạn gọi một phương thức intern()trên một chuỗi, nó:
  • Nhìn xem có chuỗi nào có văn bản này trong nhóm chuỗi không
  • Nếu có, trả về một liên kết đến nó trong nhóm
  • Nếu không, nó sẽ đặt một dòng có văn bản này vào nhóm chuỗi và trả về một liên kết tới nó.
Bằng cách áp dụng phương thức này intern()cho tham chiếu chuỗi được tạo thông qua new, chúng ta có thể so sánh nó với tham chiếu chuỗi từ String Pool'a qua ==.
public class Main {

   public static void main(String[] args) {

       String s1 = "JavaRush is the best site to learn Java!";
       String s2 = new String("JavaRush is the best site to learn Java!");
       System.out.println(s1 == s2.intern());
   }
}
Đầu ra của bảng điều khiển:

true
Trước đây, khi chúng ta so sánh chúng mà không có intern(), kết quả là sai. Bây giờ phương thức này intern()đã kiểm tra xem có dòng nào có nội dung "JavaRush - trang web tốt nhất để học Java!" trong nhóm chuỗi. Tất nhiên là nó ở đó: chúng tôi đã tạo ra nó khi chúng tôi viết
String s1 = "JavaRush is the best site to learn Java!";
Chúng tôi đã kiểm tra xem tham chiếu s1và tham chiếu được phương thức trả về s2.intern()có trỏ đến cùng một vùng trong bộ nhớ hay không và tất nhiên là như vậy :) Để tóm tắt, hãy ghi nhớ và sử dụng quy tắc chính: Để so sánh các chuỗi, LUÔN LUÔN sử dụng bằng() phương pháp! Khi so sánh các chuỗi, bạn hầu như luôn muốn so sánh văn bản của chúng chứ không phải các liên kết, vùng bộ nhớ, v.v. Phương thức bằng() thực hiện chính xác những gì bạn cần. Dưới đây là một số link để bạn có thể tự học:
Bình luận
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION