25 Kasım 2014 Salı

Equals İşlemleri

Not : Bu yazıyla ilgili olarak basit hash fonksiyonlarını açıklayan Kripto başlıklı yazıya göz atabilirsiniz.

Giriş
Java ve C# dillerinde Equals() ve HashCode() metodları, özellikle hash tabanlı veri yapılarında kullanılıyorlar. Dolayısıyla yazdığımız sınıflarda bazen Equals ve HashCode metodlarını override etmek gerekir.

HashCode 32 bittir
Java'daki HashCode() ve C#'taki GetHashCode() 32 bitlik bir int döndürürler. Dolayısıyla long gibi 64 bit olan tiplerde hash çakışması kaçınılmazdır. Buna Pigeonhole principle denir.

Tek Bir Alana Göre HashCode Kullanmak
Eğer sınıfın tek bir alanına göre hash almak istersek yapmamız gereken işlem çok basit
@Override
public int hashCode() 
{
    return someFieldValue.hashCode();
}

Bazı Sınıfların Equals Metodu Vardır
Array sınıfı
Arrays.equals(arr1,arr2)

Hash ve Sıralama Farklı Şeylerdir
Hash tabanlı veri yapılarında C# dilindeki IComparable veya Java'daki karşılık gelen arayüzü kullanmak işe yaramaz, çünkü bu arayüz sıralama yapmak için işe yarar.

Her iki yöntem farklı amaçlar için kullanılsa da  Equals ve IComparable arayüzlerinin ortak noktası mevcut. Bu da iki nesnenin birbirlerine eşit olup olmadığının belirlenmesi.

İşte bu noktada bazı sınıflarda equals() ve compareTo() metodları tutarsız sonuçlar dönebiliyor. Örneğin equals() metodu iki sınıfı farklı olarak görürken, compareTo metodu eşitmiş gibi sonuç dönebiliyor.
Aşağıda bu durumu gösteren bir örnek var.

BigDecimal
equals() metodu - kullanmamak daha iyi
BigDecimal sınıfının boolean equals(Object x) metodunu kullanırken dikkatli olmak lazım çünkü scale değerleri farklı olan iki BigDecimal aslında matematiksel olarak aynı değere sahip olsalar bile farklı gibi değerlediriliyorlar.

Yine buradan aldığım örnekte testlerin istenilen sonucu vermediğini görmek mümkün. Çünkü BigDecimal sınıfının equals() metodu sayının nasıl temsil edildiğini dikkate alıyor.


compareTo - kullanmak daha iyi
İki BigDecimal sınıfı karşılaştırmak için int compareTo(BigDecimal val) metodunu kullanmak lazım.
Ancak org.apache.commons.lang.builder.EqualsBuilder sınıfı int compareTo(BigDecimal val) metodunu kullanmıyor. Dolayısıyla LANG-476'de gösterildiği gibi bazı durumlarda yanlış sonuç dönebiliyor.

Float veya Double
== metodu - sayıları sıralarken kullanmamak daha iyi
== metodu total ordering yöntemini esas almıyor. Total ordering yönteminde bir sayı dizisini sıralarken -0.0 < +0.0 ve NaN == NaN şeklinde kabul edilir. Matematiksel olarak -0.0 her zaman +0.0'a eşit olmasına rağmen ve NaN hiçbir şekilde bir başka NaN ile eşit olamazken, küçükten büyüğe doğru sıralamada eksi sayının artı sayıdan daha önce gelmesi ve NaN'nin bir başka NaN ile eşit kabul edilmesi istenir. İşte bu yüzden sayıları sıralarken == operatörü yerine compare() metodunu kullanmak lazım.
Float.NaN == Float.NaN // false
-0.0d == 0.0d // true

Not : IEEE 754 standardında == karşılaştırmasının herhangi bir yanı NaN ise sonuç her zaman false döner.

If either operand is NaN, then the result of == is false but the result of != is true.
Ancak compareTo aşağıdaki gibi çalışır. Eğer iki değer birbirlerinden küçük veya büyük değilse, -0.0, +0.0, veya NaN ise bit gösterimleri karşılaştırılır.



Apache Commons
EqualsBuilder ve HashCodeBuilder
Burada Apache Commons, Eclipse tarafından üretilen kod ve Guava'yı karşılaştıran örnekler var.
EqualsBuilder ve HashCodeBuilder aşağıdaki gibi fluent interface şeklinde kullanılıyorlar.

EqualsBuilder().append(...).append(...).isEquals()
HashCodeBuilder().append(...)..append(...).toHashCode() ;
şeklinde kullanılıyor.

Guava
Objects.equal ve Objects.hashCode metodları kullanılıyor. Örnek:

Guava ile gelen HashCode sınıfı içinde bir byte array tutuyor.

C#
C# Equals() ve HashCode() metodları Java ile aynı. Burada bir  örnek var.

Hiç yorum yok:

Yorum Gönder