20 Aralık 2016 Salı

Küçük Detayların Büyük Etkileri

0 yorum
English version is at DZone

Soru:
 Aşağıdaki kod parçası ile kaç satır yazı yazılır?
public static void main(String[] args){ for (int i = Integer.MIN_VALUE; i <= Integer.MAX_VALUE; i++) { System.out.println(i); } }

a) Sonsuz sayıda
b) (2147483647 *2) +1
c) 0
d) Derleme Hatası
Birçoğumuz, dikkat etmezsek, cevabın “b” şıkkı olduğu yanılgısına kapılabiliriz. Ama aşağıdaki diğer bir kod parçası cevabın “a” şıkkı olduğuna bizi ikna edecektir.
public static void main(String[] args){ System.out.println(Integer.MIN_VALUE); System.out.println(Integer.MAX_VALUE); System.out.println(Integer.MAX_VALUE + 1); }


Çıktı:
-2147483648
2147483647
-2147483648

Yukarıdaki kod parçası, i değişkeninin değeri Integer.MAX_VALUE değerine eşit olana kadar beklendiği şekilde çalışacaktır. Bu noktada i değişkenin değeri yani Integer.MAX_VALUE ekrana yazılır. Mevcut iterasyonun kontrolü geçildikten sonra, i değişkeninin değeri 1 arttırılır. Burada i değişkeninin yeni değerinin Integer.MAX_VALUE+1 olduğunu düşünebiliriz, ama değil, yeni değer Integer.MIN_VALUE ’dur. Integer.MIN_VALUE değeri Integer.MAX_VALUE değerinden küçük olduğundan iterasyon sonsuza kadar devam eder. 
Bu durumu anlamak için byte’ların ve bit’lerin dünyasına dalmamız gerekecektir. Java’da int veri tipi 32 bit’e kadar olan değerleri tutabilmektedir. Tutabildiği en küçük değer de Integer.MIN_VALUE, yani -2147483648 değeridir. İkili sayı sistemindeki ifadesi ile 1000 0000 0000 0000 0000 0000 0000 0000 ’dır. Burada en soldaki bit, işaret bitidir. 0 değeri sayının pozitif, 1 değeri ise negatif olduğunu belirtir. Yani yukarıdaki ifadede işaret biti 1’dir. Burada da görüldüğü gibi, Java sayıları tutmak için sadece 31 biti kullanmaktadır.






Buradan edindiğimiz bilgi ile, Integer.MAX_VALUE, yani 2147483647, ikili sistemdeki ifadesi ile 0111 1111 1111 1111 1111 1111 1111 1111 değerine 1 değerini ekleyelim:
Görüdüğü gibi sonuç, Integer.MIN_VALUE değeridir.
Yukarıdaki durumu ele alacak olursak, örneğin, oluşturduğumuz bir JPA entity class’ında java.lang.Integer tipindeki bir identifier ile kodumuz bir süre(belki yıllarca) beklendiği gibi çalışsa da, veritabanında oluşturacağımız kayıtların miktarı arttıkça yukarıda bahsettiğimiz problemle karşılaşabiliriz. Hiç ara vermeden, her milisaniyede yeni bir identifier ürettiğimiz bir uygulamada, Integer tipi bizi neredeyse 2 ay idare edebilecekken, Long tipi ile 300 milyon yıl gidebiliriz.
Sonuç olarak, doğru veri tiplerinin seçimi küçük bir detay olarak düşünülebilse de, başımızı ağırtacak büyük etkilere neden olabilirler. Hocamızın, “Mühendislik becerisi uç noktalarda belli olur” sözünü de bu vesileyle  paylaşmak isterim.
ali kemal taşçı

22 Mart 2016 Salı

Java Notları – Parasal Problemler

2 yorum
English version is at DZone

İlkokulda çözmeye alıştığımız basit bir problemi bir de Java’da yazacağımız kodla çözmeye çalışalım.

Klasik problem şöyle:

Ali’nin cebinde 1 TL’si vardır, Ali cebindeki para ile bakkaldaki, 10 Kr’luk şekerlerden kaç tane alabilir?

Java Kodumuz:
public static void main(String[] args) { float alininParasi = 1.00f; float sekerinFiyati = 0.10f; int aldigiSekerlerinSayisi = 0; System.out.println("Ali'nin cebinde, " + alininParasi + " TL'si vardir."); System.out.println("Bakkaldaki sekerin tanesi " + sekerinFiyati + " TL'dir."); while (alininParasi >= sekerinFiyati) { aldigiSekerlerinSayisi++; alininParasi -= sekerinFiyati; } System.out.println("Hesaplamamiza gore, Ali bakkaldan " + aldigiSekerlerinSayisi + " seker alabilir ve cebinde de " + alininParasi + " TL'si kalir."); }
Program Çıktısı:

Ali'nin cebinde, 1.0 TL'si vardir.
Bakkaldaki sekerin tanesi 0.1 TL'dir.
Hesaplamamiza gore, Ali bakkaldan 9 seker alabilir ve cebinde de 0.09999993 TL'si kalir.

Nasıl olur?

Burada, beklentimizden farklı bir sonuç görmemizin nedeni, kodumuzda parasal değerleri float(veya abisi double) veri tiplerinde tutmamızdır. Java’da float ve double veri tipleri, bilimsel ve mühendislik hesaplamalarında, hızlı ve doğru sonuçlar elde etmek üzere tasarlanmışlardır. Bu sebepten, tam(exact) sonuçlar beklediğimiz parasal hesaplamalarda bu veri tiplerini kullanmamamız gerekir. Bunun yerine BigDecimal, int veya long veri tiplerini kullanmamız gerekir.

Yukarıdaki Java kodumuzu float yerine BigDecimal kullanarak tekrar yazalım:
public static void main(String[] args) { BigDecimal alininParasi = new BigDecimal("1.00"); BigDecimal sekerinFiyati = new BigDecimal("0.10"); int aldigiSekerlerinSayisi = 0; System.out.println("Ali'nin cebinde, " + alininParasi + " TL'si vardir."); System.out.println("Bakkaldaki sekerin tanesi " + sekerinFiyati + " TL'dir."); while (alininParasi.compareTo(sekerinFiyati) >= 0) { aldigiSekerlerinSayisi++; alininParasi = alininParasi.subtract(sekerinFiyati); } System.out.println("Hesaplamamiza gore, Ali bakkaldan " + aldigiSekerlerinSayisi + " seker alabilir ve cebinde de " + alininParasi + " TL'si kalir."); }
Program Çıktısı:

Ali'nin cebinde, 1.0 TL'si vardir.
Bakkaldaki sekerin tanesi 0.1 TL'dir.
Hesaplamamiza gore, Ali bakkaldan 10 seker alabilir ve cebinde de 0.0 TL'si kalir.

Evet şimdi oldu.

Burada BigDecimal kullanımı ile ilgili iki dezavantajdan bahsedebiliriz:
  1.  Primitive tipler kadar kullanışlı değil
  2.  Yavaş

Basit hesaplamalar için ikincisi pek canımızı sıkmasa da, ilki bazen sinir bozucu olabilir. 

BigDecimal’e alternatif olarak, int veya long veri tiplerini kullanmayı seçebiliriz. Bu durumda ondalık kısımları bizim yönetmemiz gerekiyor. Yani yukarıdaki örneğimizde hesaplamalarımızı lira olarak değil de kuruş olarak yapmamız gerekir:
public static void main(String[] args) { int alininParasi = 100; int sekerinFiyati = 10; int aldigiSekerlerinSayisi = 0; System.out.println("Ali'nin cebinde, " + (alininParasi*0.01) + " TL'si vardir."); System.out.println("Bakkaldaki sekerin tanesi " + (sekerinFiyati*0.01) + " TL'dir."); while (alininParasi >= sekerinFiyati) { aldigiSekerlerinSayisi++; alininParasi -= sekerinFiyati; } System.out.println("Hesaplamamiza gore, Ali bakkaldan " + aldigiSekerlerinSayisi + " seker alabilir ve cebinde de " + (alininParasi *0.01) + " TL'si kalir."); }
Program Çıktısı:

Ali'nin cebinde, 1.0 TL'si vardir.
Bakkaldaki sekerin tanesi 0.1 TL'dir.
Hesaplamamiza gore, Ali bakkaldan 10 seker alabilir ve cebinde de 0.0 TL'si kalir.

BigDecimal ve int veri tipleri arasındaki performans farkını, Ali’nin parasını 1.000.000 TL’ye çıkardığımız aşağıdaki program çıktılarında görebiliriz.

Program Çıktısı(BigDecimal):

Ali'nin cebinde, 1000000.0 TL'si vardir.
Bakkaldaki sekerin tanesi 0.1 TL'dir.
Hesaplamamiza gore, Ali bakkaldan 10000000 seker alabilir ve cebinde de 0.0 TL'si kalir.
islem suresi: 460 ms

Program Çıktısı(int):

Ali'nin cebinde, 1000000.0 TL'si vardir.
Bakkaldaki sekerin tanesi 0.1 TL'dir.
Hesaplamamiza gore, Ali bakkaldan 10000000 seker alabilir ve cebinde de 0.0 TL'si kalir.

ali kemal taşçı