Sayfalar

autoboxing etiketine sahip kayıtlar gösteriliyor. Tüm kayıtları göster
autoboxing etiketine sahip kayıtlar gösteriliyor. Tüm kayıtları göster

4 Aralık 2015 Cuma

Java Notları – Overloading

0 yorum
English version is at DZone

Java 1.5  öncesinde primitive tipler, reference tiplerinden tamamen farklıydı. Ama artık autoboxing kavramıyla, bu farkın ortadan kalkması, birçok sorunu da beraberinde getirmiştir.
Bu sorunlardan birini aşağıdaki kod parçası ile açıklayalım:


import java.util.ArrayList; import java.util.List; import java.util.Set; import java.util.TreeSet; public class SetAndList{ public static void main(String[] args) { Set<Integer> set = new TreeSet<Integer>(); List<Integer> list = new ArrayList<Integer>(); for (int i = -2; i < 3; i++) { set.add(i); list.add(i); } for (int i = 0; i < 3; i++) { set.remove(i); list.remove(i); } System.out.println(set + " " + list); } }

Birçoğumuz yukarıdaki kod parçasının çıktısının aşağıdaki gibi olacağını düşünebiliriz:
[-2, -1] [-2, -1]
Ama maalesef kod düşündüğümüzden farklı bir şekilde çalışarak, aşağıdaki çıktıyı üretecektir:
[-2, -1] [-1, 1]
Yukarıdaki durumun, Java 1.5 ile hayatımıza giren “autoboxing” özelliği ile “overloading” kavramlarının dikkatsiz kullanımından kaynaklandığını söyleyebiliriz.
Yukarıdaki koddaki “set.remove(i)” satırı ile, “remove(E)” methodu çağrılmaktadır. Buradaki “E”, set’in element tipi olan “Integer”’dır ve int değeri auotoboxing ile Integer değere çevrilir. Bu da beklediğimiz bir durumdur, yani kod set’in içindeki pozitif değerleri set’ten çıkarır.
Diğer taraftan, koddaki “list.remove(i)” satırı, overload edilen “remove(int i)” methodunu çağırır. Bu method ise, listedeki belirtilen posizyondaki değeri siler. Yani yukarıdaki kod, "list” ’ in içeriğinin [ -2, -1, 0, 1, 2 ] olduğu durumda, önce “0” pozisyonundaki, sonra “1” ve sonra da “2” poziyonundaki değeri listeden çıkarır. Sonunda listenin içeriği [-1, 1] olarak kalır. Buradaki karışıklığı önlemek için, “list.remove(i)” satırındaki i’yi Integer’a cast ederek doğru overload edilmiş methodun çağrılmasını sağlayabiliriz.

for (int i = 0; i < 3; i++) { set.remove(i); list.remove((Integer)i); // veya list.remove(Integer.valueOf(i)); }


Bu düzeltmeden sonra, kodumuz beklediğimiz gibi aşağıdaki çıktıyı verebilir:
[-2, -1] [-2, -1]
Yukarıdaki karışıklığın nedeni, compiler’ın List interface’inin overload edilen iki adet remove methodunu(remove(E) ve remove(int)) autoboxing özelliği nedeniyle karıştırmış olmasından kaynaklanmaktadır. Java 1.5 sonrası, generic ve autoboxing kavramları, List interface’ini bozdu diyebiliriz.
Buradan çıkaracağımız dersleri aşağıdaki gibi özetleyebiliriz:

  • Java class’larımızdaki method’larımızı overload edebiliyor olmamız, onları overload etmemiz gerektiği anlamına gelmemektedir.

  • Genel olarak, aynı sayıda parametre içeren methodlarımızı overload etmekten kaçınmamız, yani methodlarımız için anlamlı ve farklı isimler kullanmamız gerekmektedir. İlla bu şekilde yapmamız gerekiyorsa da, methodların aynı parametrelerde, aynı davranışı göstermelerine dikkat etmemiz gerekmektedir.

Yukarıdaki maddelere dikkat etmediğimiz durumda, methodları kullanacak kişilere çok dikkatli olmaları gerektiğini anlatmamız gerekecektir. Aksi halde methodların neden çalışmadıklarını anlamakta zorlanacaklardır.

Yararlandığım Kaynaklar
Efective Java 2nd Edition, J. Bloch

ali kemal taşçı

7 Ekim 2015 Çarşamba

Java Performans Notları: Autoboxing / Unboxing

0 yorum
English version is at DZone

Aşağıdaki kod parçasında sadece 1 karakteri değiştirerek, işlem süresini yaklaşık 5’te 1’ine indirebileceğimizi söylesem ne düşünürsünüz?

long t = System.currentTimeMillis(); Long sum = 0L; for (long i = 0; i < Integer.MAX_VALUE; i++) { sum += i; } System.out.println("toplam:" + sum); System.out.println("işlem süresi: " + (System.currentTimeMillis() - t) + " ms") ;

Çıktı:
toplam:2305843005992468481
işlem süresi:  6756 ms



Yukarıdaki kod parçası hakkında biraz düşündükten sonra, aşağıdaki daha hızlı çalışan kod parçasını inceleyebilirsiniz.

long t = System.currentTimeMillis(); //Long sum = 0L; long sum = 0L; for (long i = 0; i < Integer.MAX_VALUE; i++) { sum += i; } System.out.println("toplam:" + sum); System.out.println("işlem süresi: " + (System.currentTimeMillis() - t) + " ms") ;

Çıktı:
toplam:2305843005992468481
işlem süresi: 1248 ms

Yukarıdaki hız farkının, Java 1.5 ile hayatımıza giren “Autoboxing” özelliğinin dikkatsiz kullanımından kaynaklandığını söyleyebiliriz.

Farkın nedenine devam etmeden, öncelikle Java’da “Autoboxing” ve “Unboxing” kavramını açıklamaya çalışalım. 

Java’da tipler primitive ve referans olmak üzere ikiye ayrılmaktadır. Java’da 8 adet primitive tip bulunmaktadır. Her primitive tipe karşılık gelen de bir referans tipi(wrapper class) vardır. 

Primitive tipler
Referans Tipler(Wrapper Class)
boolean
Boolean
byte
Byte
char
Character
float
Float
int
Integer
long
Long
short
Short
double
Double

Aşağıdaki örnek kod parçasında autoboxing ve unboxing kavramlarına birer örnek görebilirsiniz. Burada Long bekleyen bir ArrayList’e long bir değer eklenmektedir. Java 1.4’te bu işlemin yapılabilmesi için primitive değerin uygun bir referans tip içine eklenerek(boxing) yapılması gerekirdi. Java 1.5 itibariyle bu işlemi compiler bizim için yapmaktadır. Böylece fazladan kod yazmak zorunda kalmıyoruz.

Yani aşağıdaki kod;

List<Long> longList = new ArrayList<>(); long i = 4; longList.add( i ); //autoboxing long j = longList.get( 0 ); //unboxing

compiler tarafından otomatik olarak aşağıdaki koda çevrilmektedir.

List<Long> longList = new ArrayList<>(); long i = 4; longList.add(Long.valueOf( i ) ); long j = longList.get( 0 ).longValue();

Buradan ilk kodumuzun da aslında aşağıdaki gibi bir koda çevrildiğini söyleyebiliriz. Bu işlemin uzun sürmesinin nedenini de kod çalışırken, 2147483647 adet gereksiz Long instance’ı oluşturmaya çalışması olarak açıklayabiliriz.

long t = System.currentTimeMillis(); Long sum = 0L; for (long i = 0; i < Integer.MAX_VALUE; i++) { sum += new Long(i); } System.out.println("toplam:" + sum); System.out.println("işlem süresi: " + (System.currentTimeMillis() - t) + " ms") ;

Çıktı:
toplam:2305843005992468481
işlem süresi: 6764 ms

Sonuç olarak, daha hızlı çalışacak Java kodları yazmak için, autoboxing ve unboxing durumlarını dikkate almalıyız. Gereksiz instance oluşturma işlemlerinden kaçınmalı, hesap gerektiren çoklu işlemlerde primitive tipleri tercih etmeliyiz diyebiliriz.

Yararlandığım Kaynaklar
Autoboxingand Unboxing
Autoboxing


ali kemal taşçı