Pure Java – 67 Generics & Collections – hashCode & equals & toString

Merhaba Arkadaslar,

Bu bolumde java.lang.Object sinifinda yer alan toString , equals ve hashCode metotlarini inceleyecegiz. Hatirlayacagimiz gibi java.lang.Object sinifi , Java’da sinif hiyerarsisinin en tepesindedir ve varsayilan olarak her sinif icin IS-A Object onermesi dogrudur. Bununla birlikte java.lang kutuphanesi varsayilan olarak import edilir.

toString
toString metodu objenin temsili(representation) string degerini doner.
Bu metodun override edilmesi onerilir neden mi su kucuk ornegimizi inceleyelim.
Basit bir ornek yazalim ;

public class Animal {

	int weight = 20;
	int height = 10;

	public static void main(String[] args) {
		Animal animal = new Animal();
		System.out.println(animal);
	}

}

System.out.println metodunu kullandigimizda bu metot dolayli olarak toString metodunu cagirmaktadir. paketismi+sinifismi+@Sayi seklinde bir cikti verecektir. Bu okunabilirlik acisindan pek hos durmamaktadir. toString metodunu inceledigimizde neden boyle bir cikti verdigini gorebiliriz.

    public String toString() {
	return getClass().getName() + "@" + Integer.toHexString(hashCode());
    }

Eclipse’te alt+shift+s yardimi ile Generate toString()… sekmesini kullanabiliriz ve otomatik olarak override edebiliriz.

public class Animal {

	int weight = 20;
	int height = 10;

	public static void main(String[] args) {
		Animal animal = new Animal();
		System.out.println(animal);
	}

	@Override
	public String toString() {
		return "Animal [weight=" + weight + ", height=" + height + "]";
	}
}

Override ettigimiz toString metodunda return ifadesi ciktimiz olacaktir. Burayi diledigimiz gibi degistirebiliriz.

equals
Onceki bolumlerde equals metodunu ve == kontrolunu inceledik. Hatirlayacagimiz gibi == kontrolu referans tipteki degiskenler icin ilgili referans degiskenler ayni objeyi mi referans ediyor kontrolu yapmaktaydi. Eger bu iki referans degisken ayni objeyi referans ediyorsa true aksi takdirde false degeri donmekteydi. Wrapper ve String siniflar icin ozel durumlar soz konusuydu. Immutable ozelligini ve Pool konusunu unutmayalim !

Eger iki referans degiskeninin ayni objeyi gosterip gostermedigi kontrolu gerekiyorsa == , anlamsal olarak/meaningfully esitlik kontrolu gerekiyorsa equals kullanilmalidir.

public class EqualsTest {

	public static void main(String[] args) {
		Integer i1 = new Integer(10);
		Integer i2 = new Integer(10);

		System.out.println(i1 == i2);
		// i1 ve i2 referans degiskenleri farkli objeleri gostermektedir. Bu
		// nedenle false deger doner.

		System.out.println(i1.equals(i2));
		// equals metodu referans kontrolu yapmaz.Anlamsal olarak/meaningfully
		// esitlik kontrolu yapar. true deger donecektir.
	}
}

Integer sinifinda equals metodu override edilmistir ;

    public boolean equals(Object obj) {
        if (obj instanceof Integer) {
            return value == ((Integer)obj).intValue();
        }
        return false;
    }

Object sinifindaki equals metodu ;

    public boolean equals(Object obj) {
        return (this == obj);
    }

Integer sinifi gibi diger Wrapper siniflarinda ve String sinifinda da equals metodu override edilmis durumdadir.Peki override edilmeyen durumda durum nasil olmaktadir ;

public class Animal {

	public static void main(String[] args) {
		Animal animal = new Animal();
		Animal animal2 = new Animal();
		Animal animal3 = animal;

		System.out.println(animal == animal2);
		// == kontrolu false donecektir. == kontrolu bu iki referans degiskenin
		// ayni objeyi gosterip gostermediginin kontrolunu yapar.

		System.out.println(animal == animal3);
		// == kontrolu animal ve animal3 referans degiskenleri icin true
		// donecektir.
		// animal referans degiskeni animal3 referans degiskenine atandiktan
		// sonra animal3 ve animal referans degiskeni ayni objeyi referans
		// etmektedir.

		System.out.println(animal.equals(animal2));
		// java.lang.Object sinifinda bulunan equals metodu varsayilan olarak ==
		// de oldugu gibi referans kontrolu yapar.
		// Burada false donecektir

		System.out.println(animal.equals(animal3));
		// == kontrolu true dondugu icin yani animal ve animal3 ayni objeyi
		// gosterdikleri icin
		// equals da true donecektir.
	}
}

equals metodunu override etmedigimiz surece varsayilan durum icin yani java.lang.Object sinifinda yer alan haliyle kullanabiliriz. java.lang.Object sinifinda equals metodu == kontrolu gibi calismaktadir yani kontrol edilen 2 referans degisken ayni objeyi gosteriyorsa true aksi durumda false deger donecektir.

public class Animal {

	int weight;
	int height;

	public Animal(int weight, int height) {
		this.weight = weight;
		this.height = height;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		Animal other = (Animal) obj;
		if (height != other.height)
			return false;
		if (weight != other.weight)
			return false;
		return true;
	}

	public static void main(String[] args) {
		Animal animal = new Animal(10, 20);
		Animal animal2 = new Animal(10, 20);

		System.out.println(animal == animal2);
		// == kontrolu false doner. animal ve animal2 ayni objeye referansta
		// bulunmuyor.
		System.out.println(animal.equals(animal2));
		// true deger donecektir. Override edilen equals metodunda height ve weight degerlerinin
		// ayni olmasi 2 Animal objesinin anlamsal olarak/meaningfully esit
		// olmasini saglar.
		// Eclipse ile otomatik olarak olusturduk. Dilersek kendimiz degisiklik
		// yapabiliriz.

		Animal animal3 = new Animal(20, 40);
		Animal animal4 = new Animal(40, 70);

		System.out.println(animal3 == animal4);
		System.out.println(animal3.equals(animal4));
	}

}

Unutmayalim equals metodu parametre olarak java.lang.Object sifini tipinde bir degisken almaktadir. Sonrasinda cast etmek gerekmektedir.

equals Contract
equals metodunun sahip olmasi gereken su ozellikler mevcuttur;

reflexive(yansimali)

x.equals(x); //true olmalidir.

symmetric(simetrik)

x.equals(y); //true ise 
y.equals(x) //true olmalidir.

transitive(gecisli)

x.equals(y); //true 
y.equals(z); //true ise
x.equals(z); //true olmalidir

consistent(tutarli)
equals metodunun bir cok defa kullanilmasinda sonuc degismemelidir.Tutarli olmalidir.

x.equals(y); // true ise her zaman true donmelidir.

null reference

x.equals(null); // false deger donmelidir.

hashCode
java.lang.Object sinifinda yer alan hashCode metodu bir native metottur. Bu metot JVM tarafindan uygulanir/implements.

Her sinif icin varsayilan olarak IS-A Object onermesi dogrudur. Dolayisiyla her sinif varsayilan olarak hashCode metodunu kullanabilir.

    public native int hashCode();

hashCode 32 bit signed/isaretli tekil bir degerdir/sayidir. hasCode degerini objeler icin ID degeri olarak dusunebiliriz.

HashMap ,Hashtable HashSet gibi Collection yapilari ilgili objelerin store/ saklanmasi/ doldurulmasi konusunda hashCode degerini kullanir.

Yazinin basinda toString metodundan bahsetmistik. println metodu dolayli olarak toString metodunu cagirmaktaydi ve obje referanslarinin ciktisi olarak paketismi+sinifismi+@+hashCode(hex formatta) degerini vermekteydi. Burada hashCode degerimiz hexedecimal formattadir. Bu degerin decimal/onluk karsiligini hashCode metodunu cagirarak gorebiliriz;

public class Dog {

	public static void main(String[] args) {
		Dog dog = new Dog();
		Dog dog2 = new Dog();

		System.out.println(dog.hashCode());
		System.out.println(dog2.hashCode());

		System.out.println(dog);
		System.out.println(dog2);
	}

}

hashCode metodunun override edilmesi Collection yapilarinda ozellikle gerekmektedir. hashCode degeri ilgili objeyi collection/koleksiyon/yigin arasinda yer tespitinin yapilip(locate) bulabilmemizi saglar.

hashCode metodunun uygun/appropriate ve dogru bir sekilde/correct override edilmesi onemlidir. Eger uygun bir sekilde override edilmezse verimsiz bir durum soz konusu olacaktir/inefficient.
hashCode metodu ilgili tum objeler icin ayni degeri donebilir bu legaldir fakat uygun/appropriate degildir.

public int hashCode() { return 2014; }

Peki hashCode degerinin ayni olmasi neden verimsiz olmaktadir ? Collection yapisinda bir objenin bulunmasi 2 asamalidir ;

  • Dogru bucket/sepeti bul.(hashCode)
  • Aranan dogru elamani bul(equals)

Eger yukarida oldugu gibi tum objeler icin tek bir hashCode degeri donmesi butun objelerin ayni bucket/sepet icerisinde olmasi demektir. Elimizde 1000 tane obje oldugunu dusunursek hashCode metodu bu durumda pek yardimci olmayacaktir cunku butun objeler tek bir bucket/sepet icerisinde yer almaktadir. Bunun yerine hashCode metodununun uygun bir sekilde override edilmesi sonucunda cok daha fazla bucket/sepet olacaktir. Ilgili bucket/sepeti bulduktan sonra icerisinde daha az obje yer alacaktir bu durumda aranan objeyi bulmak cok daha kolay olacaktir.

Kisacasi tum objeler icin ayni hashCode degeri verilebilir bu durum legal olmasina ragmen Collection yapisi icin yavas dolayisiyla verimsiz olacaktir.

Eger iki obje referansi icin equals kontrolu true donuyorsa hashCode kontrolu de true olmalidir.

public class EqualsTest {

	public static void main(String[] args) {
		Integer i1 = new Integer(10);
		Integer i2 = new Integer(10);

		System.out.println(i1 == i2);
		// i1 ve i2 referans degiskenleri farkli objeleri gostermektedir. Bu
		// nedenle false deger doner.

		System.out.println(i1.equals(i2));
		// equals metodu referans kontrolu yapmaz.Anlamsal olarak/meaningfully
		// esitlik kontrolu yapar. true deger donecektir.

		System.out.println(i1.hashCode());
		System.out.println(i2.hashCode());
		System.out.println(i1.hashCode() == i2.hashCode());
		// Eger iki obje referansi icin equals kontrolu true donuyorsa hashCode
		// kontrolu de true olmalidir.

	}
}

equals metodu override edilince hashCode metodunu da override etmek uygun olacaktir. Animal sinifi orneginde sadece equals metodunu override etmistik simdi de Eclipse yardimi ile 2 metodu da override edelim.

public class Animal {

	int weight;
	int height;

	public Animal(int weight, int height) {
		this.weight = weight;
		this.height = height;
	}

	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + height;
		result = prime * result + weight;
		return result;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		Animal other = (Animal) obj;
		if (height != other.height)
			return false;
		if (weight != other.weight)
			return false;
		return true;
	}

	public static void main(String[] args) {
		Animal animal = new Animal(10, 20);
		Animal animal2 = new Animal(10, 20);

		System.out.println(animal.equals(animal2));
		System.out.println(animal.hashCode());
		System.out.println(animal2.hashCode());

	}

}

Eger iki obje referansi icin equals kontrlu false donuyorsa bu durumda hashCode degeri ayni da olabilir farkli da olabilir.

Serialization soz konusu oldugunda transient olan instance degiskenler default olarak deserialization islemine tabi tutuluyordu. Bu nedenle equals ve hashCode metotlarini override ederken transient instance degiskenleri kullanmamak gerekir.

equals ve hashCode metotlari icin su onermelere uygun sekilde override islemi yapilmalidir.

x.equals(y)==true ise
x.hashCode()==y.hashCode() true olmali !


x.equals(y)== false ise
x.hashCode() == y.hashCode() true ya da false olabilir.


x.hashCode() != y.hashCode() true ise
x.equals(y) == false olmalidir

x.hashCode()== y.hashCode() true ise
x.equals(y)== true veya false olabilir.

Override the equals correctly , we are same instance of humanity.

Yazimi burada sonlandiriyorum.
Herkese Bol Javali Gunler dilerim.
Be an oracle man , import java.*;
Levent Erguder
OCP, Java SE 6 Programmer
OCE, Java EE 6 Web Component Developer

Print Friendly, PDF & Email

Leave a Reply to Tuğrul Cancel reply

Your email address will not be published. Required fields are marked *