CodeGym /Kurslar /JAVA 25 SELF /Comparable interfeysi: reallaşdırma, compareTo

Comparable interfeysi: reallaşdırma, compareTo

JAVA 25 SELF
Səviyyə , Dərs
Mövcuddur

1. Obyektlərin müqayisəsi problemi

Xatırlatma: istinadların müqayisəsi və obyektlərin müqayisəsi

Artıq bilirsiniz ki, Java-da == operatoru obyektlərlə işləyərkən onların istinadlarını müqayisə edir — yəni yaddaşda eyni ünvanda olub-olmadıqlarını. Eyni sahələri olan iki obyekt, lakin new vasitəsilə yaradılıbsa, == üçün fərqli olacaq.

Person p1 = new Person("Sasha", 20);
Person p2 = new Person("Sasha", 20);

System.out.println(p1 == p2); // false — bunlar yaddaşda fərqli obyektlərdir!

Əgər məzmununa görə bərabər olub-olmadığını bilmək istəyiriksə, equals(), hashCode() istifadə edirik... bunu artıq bilirsiniz. Bəs “kim daha yaşlıdır”, “kim daha gəncdir”, “əlifbaca kim daha əvvəldir” kimi suallara necə baxaq? Məsələn, istifadəçilər siyahısını yaşa və ya ada görə çeşidləmək üçün.

Obyektlərin çeşidlənməsi və axtarışı zərurəti

Tutaq ki, bizdə istifadəçilərin siyahısı var və onları yaşa görə çeşidləmək istəyirik:

List<Person> people = new ArrayList<>();
people.add(new Person("Vasya", 25));
people.add(new Person("Petya", 20));
people.add(new Person("Katya", 30));

// Necə çeşidləyək?
Collections.sort(people); // Aha! Java Person-u necə müqayisə etməli olduğunu bilmir!

Kompilyator dərhal şikayət edəcək: Person sinfi Comparable interfeysini reallaşdırmır. Java beynimizi oxuya bilmir və Person üçün “böyük” və ya “kiçik” nə deməkdir — bilmir. Bunu ona öyrətmək üçün müqayisə qaydalarını açıq şəkildə təsvir etməliyik.

2. Comparable interfeysi

İnterfeysin elan edilməsi

Comparable interfeysi Java-ya belə deməyin standart yoludur: “Mənim sinfim müqayisə oluna bilər və bunu belə etmək lazımdır”.

public interface Comparable<T> {
    int compareTo(T o);
}

Köhnə tanış a.compareTo(b) aşağıdakıları qaytaracaq:

  • mənfi ədəd — deməli, a b-dən “kiçikdir”.
  • Əgər 0-dırsa — obyektlər bərabər sayılır.
  • müsbət ədəda b-dən “böyükdür”.

Nümunə: Person sinfi üçün compareTo reallaşdırması

Gəlin yaşa görə müqayisə oluna bilən Person sinfi yaradaq:

public class Person implements Comparable<Person> {
    private String name;
    private int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    // Getter-lər (daha aşağıdakı nümunələr üçün)
    public String getName() { return name; }
    public int getAge() { return age; }

    // compareTo metodunun reallaşdırılması
    @Override
    public int compareTo(Person other) {
        // Yaşa görə çeşidləmə (artma sırası ilə)
        return Integer.compare(this.age, other.age);
        // Alternativ: return this.age - other.age;
    }
}

Vacib məqam: əgər azalan qaydada çeşidləmək istəyirsinizsə, sadəcə arqumentlərin yerlərini dəyişin: Integer.compare(other.age, this.age).

Analoqiya. compareTo — yarışda hakim kimidir: o, kimin qabaqda, kimin arxada, kimin isə eyni səviyyədə olduğunu dəqiq müəyyən etməlidir. Əgər bütün hakimlər (compareTo metodları) fərqli qaydada qərar versələr — xaos başlayacaq!

3. Comparable-dən istifadə

Comparable ilə kolleksiyaların çeşidlənməsi

Artıq sinfimiz Comparable-i reallaşdırdığından, çeşidləmə “qutudan çıxan” kimi işləyir:

List<Person> people = new ArrayList<>();
people.add(new Person("Vasya", 25));
people.add(new Person("Petya", 20));
people.add(new Person("Katya", 30));

Collections.sort(people); // compareTo istifadə olunur!

for (Person p : people) {
    System.out.println(p.getName() + " (" + p.getAge() + ")");
}
// Petya (20)
// Vasya (25)
// Katya (30)

Eyni şəkildə siyahının sort metodu da işləyir:

people.sort(null); // null ötürülsə, compareTo istifadə olunur

Ada görə çeşidləmə

Əgər ada görə çeşidləmək istəyiriksə — reallaşdırmanı dəyişirik:

@Override
public int compareTo(Person other) {
    return this.name.compareTo(other.name);
}

Bir neçə sahə üzrə çeşidləmə

Bəzən əvvəl bir sahəyə görə, bərabər olduqda isə digərinə görə müqayisə etmək lazımdır:

@Override
public int compareTo(Person other) {
    int cmp = Integer.compare(this.age, other.age);
    if (cmp != 0) return cmp;
    return this.name.compareTo(other.name);
}

4. Comparable reallaşdırılmasında ən yaxşı təcrübələr

Comparable müqaviləsinə əməl edin

  • Əgər a.compareTo(b) == 0-dırsa, o zaman b.compareTo(a) mütləq 0 olmalıdır.
  • Əgər a.compareTo(b) < 0-dırsa, b.compareTo(a) > 0 olmalıdır (və əksinə).
  • Əgər a.compareTo(b) == 0-dırsa, yaxşı olardı ki, a.equals(b)true olsun (amma bu sərt tələb deyil).

Bu niyə vacibdir?
Kolleksiyalar (məsələn, TreeSet, TreeMap) və çeşidləmə metodları müqavilə pozularsa, gözlənilməz davranış göstərə bilər. Məsələn, dublikatlar, əslində olmamalı olduqları halda, kollek­siyada peyda ola bilər.

equals və hashCode barədə unutmayın

Əgər compareTo reallaşdırırsınızsa, düşünün: equalshashCode düzgünmü reallaşdırılıb? Xüsusən də sinfiniz HashSet və ya Map tipli kolleksiyalarda istifadə olunacaqsa.

@Override
public boolean equals(Object o) {
    if (this == o) return true;
    if (!(o instanceof Person)) return false;
    Person other = (Person) o;
    return age == other.age && Objects.equals(name, other.name);
}

@Override
public int hashCode() {
    return Objects.hash(name, age);
}

Yoxlamasız compareTo daxilində null ola bilən sahələrdən istifadə etməyin!

Əgər sahə null ola bilərsə, təhlükəsiz müqayisələrdən istifadə edin:

@Override
public int compareTo(Person other) {
    return Objects.compare(this.name, other.name, Comparator.nullsFirst(String::compareTo));
}

Obyekt artıq çeşidlənmiş kolleksiyada olduqda, compareTo-da iştirak edən sahələri dəyişməyin

Bu, obyektin kollek­siya daxilində “itirilməsinə” səbəb ola bilər — məsələn, TreeSet və ya TreeMap-də.

5. Tədris tətbiqini inkişaf etdiririk: istifadəçilərin çeşidlənməsi

Addım 1: Sinfi təsvir edirik

public class Person implements Comparable<Person> {
    private String name;
    private int age;

    // ... konstruktor, getter-lər, compareTo, equals, hashCode ...
}

Addım 2: İstifadəçiləri əlavə edirik

List<Person> people = new ArrayList<>();
people.add(new Person("Masha", 23));
people.add(new Person("Grisha", 19));
people.add(new Person("Anya", 25));

Addım 3: Çeşidləyirik və çap edirik

Collections.sort(people);

for (Person p : people) {
    System.out.println(p.getName() + " (" + p.getAge() + ")");
}

Nəticə:

Grisha (19)
Masha (23)
Anya (25)

6. Comparable-in iş sxemi

┌────────────────────────────┐
│   Sizin sinfiniz (Person)  │
├────────────────────────────┤
│ implements Comparable      │
│   ↓                        │
│ public int compareTo(T o)  │
│   ↓                        │
│ (this < o) → -1            │
│ (this == o) → 0            │
│ (this > o) → 1             │
└────────────────────────────┘
        │
        ▼
Collections.sort(list)
        │
        ▼
   Çeşidləmə işləyir!

7. Faydalı nüanslar

Collections.sort necə işləyir

  • Əgər siyahı Comparable-i reallaşdıran obyektlərdən ibarətdirsə, çeşidləmə onların compareTo metodundan istifadə edəcək.
  • Əgər reallaşdırmırsa — kompilyasiya xətası olacaq.
  • Standart tiplər üçün (Integer, String və s.) Comparable artıq reallaşdırılıb.

Bir neçə müqayisə üsulunu etmək olar?

  • Bir sinifdə — yalnız bir “təbii sıra” Comparable vasitəsilə olur.
  • Alternativ sıralar üçün Comparator-dan istifadə edin (növbəti mühazirə).

Nümunə: sətirlər üçün compareTo

String a = "apple";
String b = "banana";
System.out.println(a.compareTo(b)); // mənfi ədəd, çünki "apple" < "banana"

Cədvəl: compareTo nə qaytarır

Müqayisə Qaytarılan dəyər
this < o
< 0
this == o
0
this > o
> 0

8. Comparable reallaşdırılmasında tipik səhvlər

Səhv №1: compareTo müqaviləsinin pozulması.
Əgər a.compareTo(b) 0 qaytarır, amma b.compareTo(a) 0 qaytarmırsa, kolleksiyalar qəribə davranacaq. Məsələn, TreeSet obyektləri fərqli hesab edib hər ikisini də əlavə edə bilər.

Səhv №2: İlkinləşdirilməmiş (null) sahələrin istifadəsi.
Müqayisə etdiyiniz sahə null ola bilirsə və siz yoxlama etmirsinizsə — NullPointerException alacaqsınız.

Səhv №3: compareTo və equals arasında uyğunsuzluq.
Əgər compareTo obyektlərin bərabər olduğunu deyirsə (0), amma equals — fərqli olduğunu (false) deyirsə, bu, kolleksiyalarla işləyərkən xətalara gətirib çıxaracaq.

Səhv №4: Obyekt çeşidlənmiş kollek­siyaya əlavə edildikdən sonra compareTo-da iştirak edən sahələrin dəyişdirilməsi.
Bu, sanki artıq əlifba sırasına görə növbədə dayanarkən pasportda soyadınızı dəyişmək kimidir. Kolleksiya obyektinizi “itirdə” bilər.

Səhv №5: Yalnız -1, 0 və ya 1 qaytarmaq.
compareTo metodu istənilən mənfi və ya müsbət ədəd qaytara bilər, mütləq -1 və ya 1 olmalı deyil. Amma sadəlik üçün tez-tez -1/0/1 istifadə olunur.

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