1. Membandingkan objek di Jawa

Di Jawa, objek dapat dibandingkan baik dengan referensi maupun nilai.

Membandingkan referensi

Jika dua variabel menunjuk ke objek yang sama di memori, maka referensi yang disimpan dalam variabel ini adalah sama. Jika Anda membandingkan variabel-variabel ini menggunakan operator persamaan ( ==), hasilnya benar, dan hasilnya masuk akal. Semuanya sederhana di sini.

Kode Keluaran konsol
Integer a = 5;
Integer b = a;
System.out.println(a == b);


true

Membandingkan dengan nilai

Tetapi Anda sering dapat menemukan situasi di mana dua variabel mengacu pada dua objek berbeda yang identik. Misalnya, dua objek string berbeda yang berisi teks yang sama.

Untuk menentukan apakah objek yang berbeda itu identik, gunakan equals()metode ini. Misalnya:

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


false
true

Metode ini equalstidak terbatas pada Stringkelas. Setiap kelas memilikinya.

Bahkan kelas yang Anda tulis sendiri, dan inilah alasannya.



2. Objectkelas

Semua kelas di Jawa mewarisi Objectkelas. Pencipta Java datang dengan pendekatan ini.

Dan jika sebuah kelas mewarisi Objectkelas tersebut, maka ia mendapatkan semua metode Objectkelas tersebut. Dan ini adalah konsekuensi utama dari warisan.

Dengan kata lain, setiap kelas memiliki metode kelas Object, meskipun kodenya tidak menyebutkannya.

Metode yang diwariskan ini termasuk metode yang terkait dengan perbandingan objek. Ini adalah equals()dan hashCode()metode.

Kode Pada kenyataannya, inilah yang akan kita miliki:
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
   }
}

Pada contoh di atas, kami membuat Personkelas sederhana dengan parameter nama dan umur, tetapi bukan metode tunggal. Tetapi karena semua kelas mewarisi Objectkelas tersebut, Personkelas secara otomatis memiliki dua metode:

metode Keterangan
boolean equals(Object obj)
Membandingkan objek saat ini dan objek yang diteruskan
int hashCode()
Mengembalikan kode hash dari objek saat ini

Ternyata benar-benar setiap objek memiliki metodenya equals, dan objek dari tipe yang berbeda dapat dibandingkan satu sama lain. Kode seperti itu akan dikompilasi dan bekerja dengan sempurna.

Kode Keluaran 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()metode

Metode equals(), yang diwarisi dari Objectkelas, mengimplementasikan algoritme paling sederhana untuk membandingkan objek saat ini dengan objek yang diteruskan: metode ini hanya membandingkan referensi ke objek.

Anda mendapatkan hasil yang sama jika Anda hanya membandingkan Personvariabel alih-alih memanggil equals()metode. Contoh:

Kode Keluaran 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

Ketika equalsmetode dipanggil a, itu hanya membandingkan referensi yang disimpan dalam avariabel dengan referensi yang disimpan dalam bvariabel.

Namun, perbandingan bekerja secara berbeda untuk Stringkelas. Mengapa?

Karena orang-orang yang membuat Stringkelas menulis implementasi metode mereka sendiri equals().

Implementasi equals()metode

Sekarang mari kita tulis penerapan metode equals kita sendiri di kelas Person. Kami akan mempertimbangkan 4 kasus utama.

Penting:
Terlepas dari kelas mana yang menggantikan equalsmetode, selalu dibutuhkan Objectobjek sebagai argumen

Skenario 1 : objek yang sama di mana equalsmetode dipanggil juga diteruskan ke equalsmetode. Jika referensi objek saat ini dan objek yang diteruskan sama, metode harus mengembalikan true. Sebuah objek sama dengan dirinya sendiri.

Dalam kode akan terlihat seperti ini:

Kode Keterangan
public boolean equals(Object obj)
{
   if (this == obj)
    return true;

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


Bandingkan referensi

Skenario 2 : nullditeruskan ke equalsmetode — kita tidak punya apa-apa untuk dibandingkan. Objek di mana equalsmetode dipanggil pasti bukan nol, jadi kita perlu kembali falsedalam kasus ini.

Dalam kode akan terlihat seperti ini:

Kode Keterangan
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 referensi


Apakah objek yang diteruskan null?

Skenario 3 : referensi ke objek yang bukan a Personditeruskan ke equalsmetode. Apakah Personobjek sama dengan bukan Personobjek? Itu adalah pertanyaan bagi pengembang kelas Personuntuk memutuskan sesuai keinginannya.

Tapi biasanya objek harus dari kelas yang sama untuk dianggap sama. Oleh karena itu, jika sesuatu selain objek kelas Personditeruskan ke metode equals kita, maka kita akan selalu mengembalikan false. Bagaimana Anda bisa memeriksa jenis objek? Itu benar — dengan menggunakan instanceofoperator.

Inilah tampilan kode baru kita:

Kode Keterangan
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 referensi


Apakah objek yang diteruskan null?


Jika objek yang dilewatkan bukan aPerson

4. Membandingkan dua Personobjek

Apa yang akhirnya kita dapatkan? Jika kita telah mencapai akhir metode, maka kita memiliki Personreferensi objek yang bukan null. Jadi kami mengonversinya menjadi a Persondan membandingkan data internal yang relevan dari kedua objek. Dan itu adalah skenario keempat kami .

Kode Keterangan
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 referensi


Apakah objek yang diteruskan null?


Jika objek yang diteruskan bukan Person


Typecasting

Dan bagaimana Anda membandingkan dua Personobjek? Mereka sama jika memiliki nama ( name) dan umur ( age) yang sama. Kode terakhir akan terlihat seperti ini:

Kode Keterangan
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 referensi


Apakah objek yang diteruskan null?


Jika objek yang diteruskan bukan Person


Typecasting

Tapi itu belum semuanya.

Pertama, bidang nama adalah a String, jadi Anda perlu membandingkan bidang nama dengan memanggil equalsmetode.

this.name.equals(person.name)

Kedua, namebidangnya mungkin null: dalam hal ini, Anda tidak dapat memanggilnya equals. Anda memerlukan pemeriksaan tambahan untuk null:

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

Konon, jika bidang nama ada nulldi kedua Personobjek, maka namanya masih sama.

Kode untuk skenario keempat mungkin terlihat 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 usia tidak sama,
langsung return false

Jika this.namesama dengan null, tidak ada gunanya membandingkan menggunakan equalsmetode ini. Di sini bidang kedua namesama dengan null, atau tidak.

Bandingkan dua bidang nama menggunakan equalsmetode.


5. hashCode()metode

Selain equalsmetode yang dimaksudkan untuk melakukan perbandingan mendetail dari semua bidang kedua objek, ada metode lain yang dapat digunakan untuk perbandingan yang tidak tepat tetapi sangat cepat: hashCode().

Bayangkan Anda mengurutkan daftar ribuan kata berdasarkan abjad, dan Anda perlu membandingkan pasangan kata berulang kali. Dan kata-katanya panjang, terdiri dari banyak huruf. Secara umum, perbandingan seperti itu akan memakan waktu yang sangat lama.

Tapi itu bisa dipercepat. Misalkan kita memiliki kata-kata yang dimulai dengan huruf yang berbeda — segera jelas bahwa kata-kata itu berbeda. Tetapi jika dimulai dengan huruf yang sama, maka kita belum bisa mengatakan apa hasilnya nanti: kata-katanya bisa sama atau berbeda.

Metode ini hashCode()bekerja dengan menggunakan prinsip yang serupa. Jika Anda memanggilnya pada suatu objek, itu mengembalikan beberapa angka - analog dengan huruf pertama dalam sebuah kata. Nomor ini memiliki sifat-sifat berikut:

  • Objek identik selalu memiliki kode hash yang sama
  • Objek yang berbeda dapat memiliki kode hash yang sama, atau kode hashnya dapat berbeda
  • Jika objek memiliki kode hash yang berbeda, maka objek tersebut pasti berbeda

Untuk membuatnya lebih jelas, mari kita membingkai ulang properti ini dalam bentuk kata-kata:

  • Kata yang identik selalu memiliki huruf pertama yang sama.
  • Kata yang berbeda bisa memiliki huruf pertama yang sama, atau huruf pertamanya bisa berbeda
  • Jika kata memiliki huruf pertama yang berbeda, maka kata tersebut pasti berbeda

Properti terakhir digunakan untuk mempercepat perbandingan objek:

Pertama, kode hash dari kedua objek dihitung. Jika kode hash ini berbeda, maka objeknya pasti berbeda, dan tidak perlu membandingkannya lebih jauh.

Tetapi jika kode hashnya sama, maka kita masih harus membandingkan objek menggunakan metode yang sama.



6. Kontrak dalam kode

Perilaku yang dijelaskan di atas harus diterapkan oleh semua kelas di Jawa. Selama kompilasi, tidak ada cara untuk memeriksa apakah objek dibandingkan dengan benar.

Pemrogram Java memiliki kesepakatan universal bahwa jika mereka menulis implementasi metode equals() mereka sendiri dan dengan demikian mengesampingkan implementasi standar (di kelas Object), mereka juga harus menulis implementasi metode mereka sendiri hashCode()sedemikian rupa sehingga aturan yang disebutkan di atas adalah puas.

Pengaturan ini disebut kontrak .

Jika Anda hanya menerapkan equals()atau hanya hashCode()metode di kelas Anda, maka Anda melanggar kontrak (Anda telah melanggar perjanjian). Jangan lakukan ini.

Jika pemrogram lain menggunakan kode Anda, itu mungkin tidak berfungsi dengan benar. Terlebih lagi, Anda akan menggunakan kode yang bergantung pada kepatuhan terhadap kontrak di atas.

Penting!

Saat mencari elemen, semua koleksi Java terlebih dahulu membandingkan kode hash objek, dan baru kemudian melakukan perbandingan menggunakan equalsmetode tersebut.

Itu berarti bahwa jika Anda memberikan metode pada kelas Anda sendiri equalstetapi Anda tidak menulis hashCode()metode Anda sendiri atau Anda menerapkannya secara tidak benar, koleksi mungkin tidak berfungsi dengan benar dengan objek Anda.

Misalnya, Anda mungkin menambahkan objek ke daftar lalu mencarinya menggunakan metode contains(), tetapi koleksi mungkin tidak menemukan objek Anda.