Java'da equals() ve hashCode() metodları, HashMap, HashSet ve diğer koleksiyonların doğru çalışması için son derece önemlidir. Bu soru genellikle yeni geliştiriciler tarafından göz ardı edilir, ancak bu metodların sözleşmelerinin ihlali, uygulama mantığında zor tespit edilen hatalara yol açar.
Soru Tarihi:
Java dilinde tüm sınıflar, equals() ve hashCode() metodlarını Object sınıfından miras alır. Varsayılan olarak, equals() nesne referanslarını (yani bellek içindeki fiziksel konumlarını) karşılaştırırken, hashCode() her nesne için benzersiz bir kod döner. Ancak, kullanıcı tanımlı sınıflar için genellikle nesneleri içeriklerine göre karşılaştırmak gerekir, referansa göre değil.
Problem:
Eğer equals() ve hashCode() metodları yeniden tanımlanmazsa veya yanlış bir şekilde tanımlanırsa, nesneler, hashing tabanlı koleksiyonlarda beklenmedik bir şekilde davranabilir. Bu, elemanların kaybolmasına, çoğaltılmasına veya arama hatalarına neden olur.
Çözüm:
Her iki metodu da her zaman birlikte yeniden tanımlayın, sözleşmeye sıkı bir şekilde uyarak:
a.equals(b) == true ise, a.hashCode() == b.hashCode() olmalıdır.a.equals(b) == false ise, hashCode için benzersizlik zorunlu değildir.Doğru bir uygulama örneği:
public class Person { private final String name; private final int age; public Person(String name, int age) { this.name = name; this.age = age; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Person person = (Person) o; return age == person.age && Objects.equals(name, person.name); } @Override public int hashCode() { return Objects.hash(name, age); } }
Anahtar özellikler:
equals() metodu yansıtıcı, simetrik, geçişli ve tutarlı olmalıdır.hashCode() metodu değişmeyen veriler için nesneye aynı değeri döndürmelidir.HashSet'te saklanacak sınıflarda sadece equals() kullanmak mümkün mü?
Hayır. Eğer sadece equals() yeniden tanımlanmışsa, hashing tabanlı koleksiyonlar nesnelerin benzersizliğini doğru bir şekilde belirleyemez. HashSet önce hashCode'u, sonra equals'u karşılaştırır.
equals() ve hashCode() için sınıfın tüm alanlarını kullanmak zorunlu mu?
Hayır. Sadece sınıfın mantıksal kimliği için anlamlı olanlar. Örneğin, bir nesnenin içsel, benzersiz bir kimliği varsa, bu yeterlidir.
@Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; User user = (User) o; return Objects.equals(id, user.id); }
equals() içinde doğrudan alan yerine getter'a dayanmak mümkün mü?
Genellikle evet, eğer yan etkileri yoksa ve getter kararlıysa. Ancak, getter'ın farklı çağrılarda farklı değerler döndürme riski vardır — bu durumda davranış öngörülemez hale gelecektir.
equals() yeniden tanımlandığında hashCode()'u yeniden tanımlamamış olmak.hashCode() hesaplamalarında değişken alanlar kullanmak.Bir geliştirici User sınıfını uyguluyor ve sadece equals() metodunu tanımlayarak hashCode()'u unutuyor. HashSet'e nesne eklemekte ve aramakta çiftleşmelere ve "kaybolan" elemanlara neden oluyor.
Artıları:
Eksileri:
Bir geliştirici her iki metodu da kesin sözleşmeye göre uyguluyor, eşitlik ve hashleme mantığında yalnızca kimliği kullanıyor. Koleksiyonlar beklenildiği gibi davranıyor, arama ve depolama düzgün çalışıyor.
Artıları:
Eksileri: