1. Membandingkan objek dalam Java

Di Java, objek boleh dibandingkan dengan rujukan dan nilai.

Membandingkan rujukan

Jika dua pembolehubah menunjuk ke objek yang sama dalam ingatan, maka rujukan yang disimpan dalam pembolehubah ini adalah sama. Jika anda membandingkan pembolehubah ini menggunakan operator kesamaan ( ==), anda akan menjadi benar dan keputusan itu masuk akal. Semuanya mudah di sini.

Kod Output konsol
Integer a = 5;
Integer b = a;
System.out.println(a == b);


true

Membandingkan mengikut nilai

Tetapi anda selalunya boleh menghadapi situasi di mana dua pembolehubah merujuk kepada dua objek berbeza yang serupa. Contohnya, dua objek rentetan berbeza yang mengandungi teks yang sama.

Untuk menentukan sama ada objek yang berbeza adalah sama, gunakan equals()kaedah. Sebagai contoh:

Kod Output konsol
String a = new String("Hello");
String b = new String("Hello");
System.out.println(a == b);
System.out.println(a.equals(b));


false
true

Kaedah ini equalstidak terhad kepada Stringkelas. Setiap kelas ada.

Malah kelas yang anda tulis sendiri, dan inilah sebabnya.



2. Objectkelas

Semua kelas di Jawa mewarisi Objectkelas tersebut. Pencipta Java menghasilkan pendekatan ini.

Dan jika kelas mewarisi Objectkelas, maka ia memperoleh semua kaedah kelas Object. Dan ini adalah akibat utama warisan.

Dalam erti kata lain, setiap kelas mempunyai kaedah kelas Object, walaupun kod mereka tidak menyebutnya.

Kaedah yang diwarisi ini termasuk kaedah yang berkaitan dengan perbandingan objek. Ini adalah equals()dan hashCode()kaedah.

Kod Pada hakikatnya, inilah yang akan kami perolehi:
class Person
{
   String name;
   int age;
}
class Person extends Object
{
   String name;
   int age;

   public boolean equals(Object obj)
   {
      return this == obj;
   }

   public int hashCode()
   {
      return address_of_object_in_memory; // This is the default implementation, but there may be a different implementation
   }
}

Dalam contoh di atas, kami mencipta Personkelas mudah dengan nama dan parameter umur, tetapi bukan satu kaedah. Tetapi kerana semua kelas mewarisi Objectkelas, Personkelas secara automatik mempunyai dua kaedah:

Kaedah Penerangan
boolean equals(Object obj)
Membandingkan objek semasa dan objek yang diluluskan
int hashCode()
Mengembalikan kod cincang objek semasa

Ternyata setiap objek mempunyai equalskaedah, dan objek dari jenis yang berbeza boleh dibandingkan antara satu sama lain. Kod sedemikian akan disusun dan berfungsi dengan sempurna.

Kod Output konsol
Integer a = 5;
String s = "Hello";
System.out.println(a.equals(s));
System.out.println(s.equals(a));


false
false
Object a = new Integer(5);
Object b = new Integer(5);
System.out.println(a.equals(b)) ;


true

3. equals()kaedah

Kaedah equals(), yang diwarisi daripada Objectkelas, melaksanakan algoritma paling mudah untuk membandingkan objek semasa dengan objek yang diluluskan: ia hanya membandingkan rujukan kepada objek.

Anda mendapat hasil yang sama jika anda hanya membandingkan Personpembolehubah dan bukannya memanggil equals()kaedah. Contoh:

Kod Output konsol
Person a = new Person();
a.name = "Steve";

Person b = new Person();
b.name = "Steve";

System.out.println(a == b);
System.out.println(a.equals(b));






false
false

Apabila equalskaedah dipanggil pada a, ia hanya membandingkan rujukan yang disimpan dalam apembolehubah dengan rujukan yang disimpan dalam bpembolehubah.

Walau bagaimanapun, perbandingan berfungsi secara berbeza untuk Stringkelas. kenapa?

Kerana orang yang mencipta Stringkelas menulis pelaksanaan kaedah mereka sendiri equals().

Pelaksanaan equals()kaedah

Sekarang mari kita tulis pelaksanaan kita sendiri kaedah sama dalam Personkelas. Kami akan mempertimbangkan 4 kes utama.

Penting:
Tidak kira kelas mana yang mengatasi equalskaedah, ia sentiasa mengambil Objectobjek sebagai hujah

Senario 1 : objek yang sama di mana equalskaedah dipanggil juga dihantar kepada equalskaedah. Jika rujukan objek semasa dan objek yang diluluskan adalah sama, kaedah mesti kembali true. Objek adalah sama dengan dirinya sendiri.

Dalam kod ia akan kelihatan seperti ini:

Kod Penerangan
public boolean equals(Object obj)
{
   if (this == obj)
    return true;

   // The rest of the code of the equals method
}


Bandingkan rujukan

Senario 2 : nulldiserahkan kepada equalskaedah — kita tiada apa-apa untuk dibandingkan. Objek di mana equalskaedah dipanggil pastinya tidak batal, jadi kita perlu kembali falsedalam kes ini.

Dalam kod ia akan kelihatan seperti ini:

Kod Penerangan
public boolean equals(Object obj)
{
   if (this == obj)
      return true;

   if (obj == null)
      return false;

   // The rest of the code of the equals method
}


Bandingkan rujukan


Adakah objek yang diluluskan null?

Senario 3 : rujukan kepada objek yang bukan a Persondihantar kepada equalskaedah. Adakah Personobjek sama dengan bukan Personobjek? Itu adalah soalan untuk pembangun kelas Personuntuk memutuskan apa yang dia mahu.

Tetapi biasanya objek mestilah daripada kelas yang sama untuk dianggap sama. Oleh itu, jika sesuatu selain daripada objek kelas Persondiserahkan kepada kaedah sama kita, maka kita akan sentiasa kembali false. Bagaimanakah anda boleh menyemak jenis objek? Betul — dengan menggunakan instanceofoperator.

Inilah rupa kod baharu kami:

Kod Penerangan
public boolean equals(Object obj)
{
   if (this == obj)
      return true;

   if (obj == null)
      return false;

   if (!(obj instanceof Person))
      return false;

   // The rest of the code of the equals method
}


Bandingkan rujukan


Adakah objek yang diluluskan null?


Jika objek yang dilalui bukan aPerson

4. Membanding dua Personobjek

Apa yang telah kita lalui? Jika kita telah mencapai penghujung kaedah, maka kita mempunyai Personrujukan objek yang bukan null. Jadi kami menukarnya kepada a Persondan membandingkan data dalaman yang berkaitan bagi kedua-dua objek. Dan itu adalah senario keempat kami .

Kod Penerangan
public boolean equals(Object obj)
{
   if (this == obj)
      return true;

   if (obj == null)
      return false;

   if (!(obj instanceof Person))
      return false;

   Person person = (Person) obj;

   // The rest of the code of the equals method
}


Bandingkan rujukan


Adakah objek yang diluluskan null?


Jika objek yang dilalui bukan Person


Typecasting

Dan bagaimana anda membandingkan dua Personobjek? Mereka adalah sama jika mereka mempunyai nama yang sama ( name) dan umur ( age). Kod akhir akan kelihatan seperti ini:

Kod Penerangan
public boolean equals(Object obj)
{
   if (this == obj)
      return true;

   if (obj == null)
      return false;

   if (!(obj instanceof Person))
      return false;

   Person person = (Person) obj;

   return this.name == person.name && this.age == person.age;
}


Bandingkan rujukan


Adakah objek yang diluluskan null?


Jika objek yang dilalui bukan Person


Typecasting

Tetapi bukan itu sahaja.

Pertama, medan nama ialah String, jadi anda perlu membandingkan medan nama dengan memanggil equalskaedah.

this.name.equals(person.name)

Kedua, namemedan itu mungkin null: dalam kes itu, anda tidak boleh memanggilnya equals. Anda memerlukan pemeriksaan tambahan untuk null:

this.name != null && this.name.equals(person.name)

Yang berkata, jika medan nama berada nulldalam kedua-dua Personobjek, maka nama itu masih sama.

Kod untuk senario keempat mungkin kelihatan seperti ini:

Person person = (Person) obj;

if (this.age != person.age)
   return false;

if (this.name == null)
   return person.name == null;

return this.name.equals(person.name);


Jika umur tidak sama,
segera return false

Jika this.namesama dengan null, tidak ada gunanya membandingkan menggunakan equalskaedah tersebut. Di sini sama ada medan kedua nameadalah sama dengan null, atau tidak.

Bandingkan dua medan nama menggunakan equalskaedah.


5. hashCode()kaedah

Sebagai tambahan kepada equalskaedah, yang bertujuan untuk melakukan perbandingan terperinci semua medan kedua-dua objek, terdapat kaedah lain yang boleh digunakan untuk perbandingan yang tidak tepat tetapi sangat cepat: hashCode().

Bayangkan anda menyusun senarai beribu-ribu perkataan mengikut abjad, dan anda perlu membandingkan pasangan perkataan berulang kali. Dan perkataannya panjang, terdiri daripada banyak huruf. Secara umumnya, perbandingan sedemikian akan mengambil masa yang sangat lama.

Tetapi ia boleh dipercepatkan. Katakan kita mempunyai perkataan yang bermula dengan huruf yang berbeza — jelas sekali bahawa ia berbeza. Tetapi jika mereka bermula dengan huruf yang sama, maka kita belum boleh mengatakan apa hasilnya: perkataan itu mungkin berubah menjadi sama atau berbeza.

Kaedah ini hashCode()berfungsi menggunakan prinsip yang sama. Jika anda memanggilnya pada objek, ia mengembalikan beberapa nombor — serupa dengan huruf pertama dalam perkataan. Nombor ini mempunyai sifat berikut:

  • Objek yang sama sentiasa mempunyai kod cincang yang sama
  • Objek yang berbeza boleh mempunyai kod cincang yang sama, atau kod cincang mereka mungkin berbeza
  • Jika objek mempunyai kod cincang yang berbeza, maka objek itu pasti berbeza

Untuk menjadikannya lebih jelas, mari kita rangka semula sifat ini dari segi perkataan:

  • Perkataan yang sama sentiasa mempunyai huruf pertama yang sama.
  • Perkataan yang berbeza boleh mempunyai huruf pertama yang sama, atau huruf pertama mereka boleh berbeza
  • Jika perkataan mempunyai huruf pertama yang berbeza, maka perkataan itu pasti berbeza

Sifat terakhir digunakan untuk mempercepatkan perbandingan objek:

Pertama, kod cincang kedua-dua objek dikira. Jika kod cincang ini berbeza, maka objeknya pasti berbeza, dan tidak perlu membandingkannya lagi.

Tetapi jika kod cincang adalah sama, maka kita masih perlu membandingkan objek menggunakan kaedah sama.



6. Kontrak dalam kod

Tingkah laku yang diterangkan di atas mesti dilaksanakan oleh semua kelas di Jawa. Semasa penyusunan, tiada cara untuk menyemak sama ada objek dibandingkan dengan betul.

Pengaturcara Java mempunyai perjanjian sejagat bahawa jika mereka menulis pelaksanaan mereka sendiri bagi kaedah equals() dan dengan itu mengatasi pelaksanaan standard (dalam Objectkelas), mereka juga mesti menulis pelaksanaan kaedah mereka sendiri hashCode()sedemikian rupa sehingga peraturan yang disebutkan di atas adalah berpuas hati.

Pengaturan ini dipanggil kontrak .

Jika anda hanya melaksanakan kaedah equals()atau hanya hashCode()kaedah dalam kelas anda, maka anda melanggar kontrak secara besar-besaran (anda telah melanggar perjanjian). Jangan buat begini.

Jika pengaturcara lain menggunakan kod anda, ia mungkin tidak berfungsi dengan betul. Lebih-lebih lagi, anda akan menggunakan kod yang bergantung pada pematuhan kontrak di atas.

Penting!

Apabila mencari elemen, semua koleksi Java mula-mula membandingkan kod cincang objek, dan hanya kemudian melakukan perbandingan menggunakan equalskaedah tersebut.

Ini bermakna jika anda memberikan kelas anda sendiri equalskaedah tetapi anda tidak menulis hashCode()kaedah anda sendiri atau anda melaksanakannya dengan salah, maka koleksi mungkin tidak berfungsi dengan betul dengan objek anda.

Sebagai contoh, anda mungkin menambah objek pada senarai dan kemudian mencarinya menggunakan contains()kaedah, tetapi koleksi mungkin tidak menemui objek anda.