CodeGym /Kurslar /Java SELF AZ /Obyektlərin Müqayisəsi

Obyektlərin Müqayisəsi

Java SELF AZ
Səviyyə , Dərs
Mövcuddur

1. Java-da obyektlərin müqayisəsi

Java-da obyektləri həm istinad, həm də dəyərə görə müqayisə etmək olar.

İstinadların müqayisəsi

Əgər iki dəyişən yaddaşda eyni obyektə işarə edirsə, o zaman bu dəyişənlərdə saxlanan istinadlar bərabərdir. Əgər bu dəyişənləri == bərabərlik operatoru ilə müqayisə etsək, nəticədə true alınacaq, bu da məntiqidir. Burada hər şey sadədir.

Kod Ekrana çıxış
Integer a = 5;
Integer b = a;
System.out.println(a == b);


true

Dəyərə görə müqayisə

Amma tez-tez belə bir vəziyyətə rast gəlmək olar ki, iki dəyişən fərqli, amma eyni obyektlərə işarə edir. Məsələn, eyni mətni saxlayan, amma fərqli obyektlərdə yerləşən iki sətir.

Fərqli obyektlərin eyniliyini müəyyən etmək üçün equals() metodundan istifadə etmək lazımdır. Misal:

Kod Ekrana çıxış
String a = new String("Salam");
String b = new String("Salam");
System.out.println(a == b);
System.out.println(a.equals(b));


false
true

equals metodu təkcə String sinifində mövcud deyil — bu metod bütöv bütün siniflərdə var.

Hətta siz yazacağınız siniflərdə də, və səbəbi də budur.



2. Object sinfi

Java-da bütün siniflər Object sinifindən irsən gəlmiş hesab olunur. Java-nın yaradıcıları bunu belə düşünüb.

Əgər bir sinif Object sinifindən irsən gəlmişdirsə, bu irsi qəbul etmiş sinifdə Object sinfinin bütün metodları avtomatik olaraq mövcud olur. Bu, irsiyyətin əsas xüsusiyyətidir.

Başqa sözlə, hər bir sinifdə, hətta kodunda bu göstərilməsə belə, Object sinfindəki bütün metodlar mövcuddur.

Bu metodlar arasında obyektlərin müqayisəsi ilə əlaqəli metodlar da var. Bunlar equals()hashCode() metodlarıdır.

Kod Necə olacaq:
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 obyektin_yaddashdakı_ünvanı; //bu default realizasiyadır, amma başqa da ola bilər }
}

Yuxarıdakı nümunədə biz nameage parametrləri ilə bir sadə Person sinfi yaratdıq, heç bir metod olmadan. Amma, bütün siniflər Object sinfindən irsən gəldiyi üçün Person sinfində iki gizli metod var:

Metod Təsvir
boolean equals(Object obj)
Mövcud obyekt ilə ötürülən obyekti müqayisə edir
int hashCode()
Mövcud obyektin hash-code qaytarır

Belə çıxır ki, equals metodları tamamilə bütün obyektlərdə mövcuddur və müxtəlif tiplərdəki obyektlər bir-biri ilə müqayisə edilə bilər, və bu mükəmməl şəkildə kompilyasiya olunub işləyəcək.

Kod Ekranda çıxış
Integer a = 5;
String s = "Salam";
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() Metodu

Object sinifindən miras alınan equals() metodu, mövcud və ötürülən obyektləri müqayisə etmək üçün ən sadə alqoritmi ehtiva edir — sadəcə onların istinadlarını müqayisə edir.

Eyni effekt Person sinfinin dəyişənlərini bir-birinə müqayisə etməklə əldə edilir, equals() metodunu çağırmağa ehtiyac yoxdur. Məsələn:

Kod Ekrana çıxış
Person a = new Person();
a.name = "Anya";

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

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






false
false

equals metodu sadəcə içərisində ab istinadlarını müqayisə edir.

Ancaq String sinifində müqayisə fərqli işləyir. Niyə?

Çünki String sinfinin yaradıcıları equals() metodunun öz implementasiyasını yazıblar.

equals() metodunun implementasiyası

Gəlin, Person sinfində equals metodunun öz implementasiyasını yazaq. 4 əsas ssenarini nəzərdən keçirək.

Vacib:
Hər hansı bir sinif üçün equals metodu yenidən müəyyən edildiyində, o həmişə Object tipli parametr qəbul edir.

Ssenari 1: equals metoduna eyni obyekt ötürülüb, hansında ki equals metodu çağırılıb. Əgər mövcud və ötürülən obyektlərin istinadları bərabərdirsə, true qaytarılmalıdır. Obyekt özü ilə üst-üstə düşür.

Kodda bu belə görsənəcək:

Kod Təsvir
public boolean equals(Object obj)
{    if (this == obj)
    return true;

   equals metodunun digər kodları
}


İstinadları müqayisə edirik

Ssenari 2: equals metoduna null istinadı ötürülüb — müqayisə edəcək heç nə yoxdur. equals metodunun çağırıldığı obyektin null olmadığı dəqiqdir, beləliklə, bu halda false döndürmək lazımdır.

Kodda bu belə görünəcək:

Kod Təsvir
public boolean equals(Object obj)
{
   if (this == obj)
      return true;

   if (obj == null)
      return false;

   equals metodunun digər kodları
}


İstinadları müqayisə edirik


Ötürülən obyekt — null?

Ssenari 3: equals metoduna ümumiyyətlə Person sinfinə aid olmayan obyekt ötürülüb. Person sinfinin obyekti qeyri-Person sinfinin obyekti ilə bərabərdir? Burada artıq qərarı Person sinfinin tərtibçisi verir — öz istəyinə uyğun şəkildə həyata keçirir.

Ancaq adətən, obyektlər bir sinifdən olduğu halda bərabər sayılır. Buna görə də, əgər bizim equals metoduna Person sinfinə aid olmayan bir obyekt ötürülübsə, həmişə false qaytaracağıq. Obyektin hansı tipdən olduğunu necə yoxlamaq olar? Düzdür: instanceof operatoru vasitəsilə.

Bizim yeni kodumuz belə görünəcək:

Kod Təsvir
public boolean equals(Object obj)
{
   if (this == obj)
      return true;

   if (obj == null)
      return false;

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

   equals metodunun digər kodları
}


İstinadları müqayisə edirik


Ötürülən obyekt — null?


Əgər ötürülən obyekt Person tipində deyilsə

4. İki Person obyektinin müqayisəsi

Nəticədə biz nə əldə etdik? Əgər metodun sonuna qədər çatmışıqsa, deməli, bizdə Person tipində obyekt və link null deyil. O zaman onu Person tipinə çeviririk və hər iki obyektin daxili hissələrini müqayisə edirik. Bu, bizim 4-cü ssenarimizdir.

Kod Təsvir
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;

   equals metodunun qalan kodu
}


Linkləri müqayisə edirik


Keçirilən obyekt — null?


Əgər keçirilən obyekt Person tipində deyilsə


Tip çevrilməsi əməliyyatı

Bəs iki Person obyektini necə müqayisə edirik? Onlar yalnız adları (name) və yaşları (age) eynidirsə, bərabər hesab olunurlar. Son kod belə görünəcək:

Kod Təsvir
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;
}


Linkləri müqayisə edirik


Keçirilən obyekt — null?


Əgər keçirilən obyekt Person tipində deyilsə


Tip çevrilməsi əməliyyatı

Amma bu hələ də hamısı deyil.

Birincisi, name sahəsinin tipi String-dir, ona görə də name sahələrini equals metodunu çağıraraq müqayisə etmək lazımdır.

this.name.equals(person.name)

İkincisi, name sahəsi null ola bilər: o halda, onun üzərində equals metodunu çağırmaq mümkün deyil. null üçün əlavə yoxlama lazımdır:

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

Lakin əgər hər iki Person obyektində name null-dırsa, onda adlar yenə də bərabər hesab olunur.

Dördüncü ssenarinin kodu, məsələn, belə görünə bilər:

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


Əgər yaşlar fərqlidirsə,
dərhal return false

Əgər this.name null-dırsa, equals ilə müqayisə etməyə ehtiyac yoxdur. Burada ya ikinci sahə name null-dır, ya da deyil.

Iki name sahəsini equals ilə müqayisə edirik.


5. hashCode() Metodu

equals metodundan başqa, hansı ki, hər iki obyektin bütün tarlalarını detal olaraq müqayisə edir, başqa bir metod da var ki, dəqiq olmasa da çox sürətli müqayisə etmək üçün istifadə olunur — bu hashCode() metodudur.

Təsəvvür edin ki, siz yüzlərlə sözdən ibarət bir siyahını əlifba sırasına görə sıralayırsınız və hər dəfə cüt olaraq sözləri müqayisə etməlisiniz. Amma sözlər uzundur və çox hərf ehtiva edir. Ümumiyyətlə, belə bir müqayisə çox vaxt aparacaq.

Ancaq bunu sürətləndirmək olar. Deyək ki, sözlər fərqli hərflərlə başlayır: dərhal aydındır ki, onlar fərqlidir. Amma eyni hərflə başlayırlarsa, zəmanət yoxdur: sonradan sözlər həm eyni, həm də fərqli ola bilər.

hashCode() metodu bənzər prinsipi tətbiq edir. Əgər onu obyektə çağırsaq, o, müəyyən bir rəqəm qaytaracaq — bu, sözün ilk hərfi analoqudur. Bu rəqəm belə xüsusiyyətlərə malikdir:

  • Eyni obyektlərdə həmişə eyni hash-code olur
  • Fərqli obyektlərdə eyni hash-code ola bilər, fərqli də ola bilər
  • Əgər obyektlərin hash-code fərqlidirsə, obyektlər dəqiq fərqlidir

Daha yaxşı başa düşmək üçün bu xüsusiyyətləri sözlərə aid olaraq yenidən yazaq:

  • Eyni sözlərdə həmişə eyni birinci hərflər olur
  • Fərqli sözlərdə eyni birinci hərf ola bilər, fərqli də ola bilər
  • Əgər sözlərin birinci hərfləri fərqlidirsə, sözlər dəqiq fərqlidir

Sonuncu xüsusiyyət obyektləri sürətli müqayisə etmək üçün istifadə olunur:

Əvvəlcə iki obyektin hash-code-ları hesablanır. Əgər bu hash-code-lar fərqlidirsə, obyektlər dəqiq fərqlidir və onları daha da müqayisə etməyə ehtiyac yoxdur.

Amma əgər hash-code-lar eynidirsə, obyektləri equals vasitəsilə yenə də müqayisə etmək lazım olur.



6. Kodda müqavilələr

Yuxarıda təsvir edilən davranışı bütün Java sinifləri həyata keçirməlidir. Obyektlərin müqayisəsinin düzgünlüyünü kompilasiya səviyyəsində yoxlamaq mümkün deyil.

Bütün Java proqramçıları razılaşıblar ki, əgər standart metodun (sinif Object-dən) əvəzinə öz equals() metodlarını yazırlarsa, həmin qaydaları qorumaq üçün həmçinin öz hashCode() metodlarını da yazmalıdırlar.

Bu razılaşma müqavilə adlanır.

Əgər sinifinizdə yalnız equals() metodunun və ya yalnız hashCode() metodunun realizasiyasını əlavə etsəniz, müqaviləni (razılaşmanı) pozmuş sayılırsınız. Belə etmək olmaz.

Əgər digər proqramçılar sizin kodunuzu istifadə etsə, o düzgün işləməyə bilər. Həmçinin, siz özünüz də yuxarıda göstərilən müqavilələr əsasında işləyən koddan istifadə edəcəksiniz.

Vacibdir!

Java-da bütün kolleksiyalar qiymət axtarışı zamanı əvvəlcə obyektlərin hash-code-nu müqayisə edir, sonra isə müqayisə üçün equals metodunu çağırır.

Ona görə də əgər öz sinifinizi yazsanız və orada yeni equals funksiyasını yaratsanız, amma hashCode() metodunu yazmasanız və ya onu səhvlə realizə etsəniz, kolleksiyalar sizin obyektlərinizlə düzgün işləyə bilməz.

Məsələn, siz obyekti siyahıya əlavə edirsiniz, sonra onu contains() metodu ilə axtarırsınız, amma kolleksiya sizin obyektinizi tapa bilmir.

Şərhlər
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION