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şçı

26 Mayıs 2015 Salı

SQL Notları: İsimlendirme Kuralları/Standartları (Naming Convention)

0 yorum
Öncelikle, yaşanmış ve gereksiz zaman almış aşağıdaki gibi bir problemi inceleyerek başlayalım:

Problemimize konu olan db tablomuz aşağıdaki gibi:

CREATE TABLE T_CHAT_UPDATE_LOOKUP ( ID NUMBER, OPERATOR_NAME VARCHAR2(100 BYTE) NOT NULL, SCENARIO_LIST VARCHAR2(1000 BYTE) NOT NULL, OG_ROUTE_LIST VARCHAR2(1000 BYTE) NOT NULL, START_DATE DATE NOT NULL, END_DATE DATE DEFAULT to_date('31.12.9999','dd.mm.yyyy'), CREATED_DATE DATE DEFAULT SYSDATE, CREATED_BY VARCHAR2(50 CHAR) DEFAULT USER NOT NULL, MODIFIED_DATE DATE, MODIFIED_BY VARCHAR2(50 CHAR) );
İçindeki örnek datalar da aşağıdaki gibi:



Aşağıdaki fonksiyonumuzla, verilen tarih aralığında geçerli olan, parametre sayısı bulunmak istenmekte:
create or replace function get_count(start_date varchar2, end_date varchar2) return number is l_count number; begin select count(*) into l_count from T_CHAT_UPDATE_LOOKUP c where c.start_date <= TO_DATE(start_date, 'yymmdd') and nvl(c.end_date, to_date('991231', 'yymmdd')) >= TO_DATE(end_date, 'yymmdd'); return l_count; end;

Bu fonksiyon aşağıdaki gibi çalıştırılmak istendiğinde:
declare l_result number; begin l_result := get_count('150501','150501'); dbms_output.put_line(l_result); end;

Şu hata alınmakta:

Fonsiyon aşağıdaki iki şekildeki gibi değiştirildiğinde ise hata alınmamaktadır.
Buradaki sorunun to_date çevrimlerinden kaynaklandığı anlaşılmaktadır.

Peki start_date ve end_date parametreleri to_date fonksiyonunun beklediği varchar2 tipinde sayısal karakterlerden oluşuyor iken ve sorgudan önce hiçbir şekilde değişikliğe uğratılmadığına göre, yukarıdaki hatayı neden alırız? 

1.
create or replace function get_count(start_date varchar2, end_date varchar2) return number is l_count number; begin select count(*) into l_count from T_CHAT_UPDATE_LOOKUP c; --where c.start_date <= TO_DATE(start_date, 'yymmdd') -- and nvl(c.end_date, to_date('991231', 'yymmdd')) >= TO_DATE(end_date, 'yymmdd'); return l_count; end;

2.
create or replace function get_count(start_date varchar2, end_date varchar2) return number is l_count number; begin select count(*) into l_count from T_CHAT_UPDATE_LOOKUP c where c.start_date <= TO_DATE('150501', 'yymmdd') and nvl(c.end_date, to_date('991231', 'yymmdd')) >= TO_DATE('150501', 'yymmdd'); return l_count; end;

Bu hata ile ilgil biraz düşündükten sonra, aşağıdaki çözümü inceleyebilirsiniz.

Çözüm:


Aslında basit olan bu problemin nedenini şöyle açıklayabiliriz:

Aşağıdaki “get_count” fonksiyonumuzun içindeki sorguda kullanılan varchar2 “start_date” ve “end_date” parametrelerinin, algısal olarak, date’e çevrilip sorguda kullanılması gerektiğini düşünürüz.
Ama gerçekte, sorgu bu şekilde gerçekleşmemektedir.

Gerçekte olan ise, sorguda önce date tipindeki “start_date” ve “end_date” kolonları yine date’e çevrilmeye çalışılmaktadır.
Çözüm olarak ise, “get_count” fonksiyonunda aşağıdaki değişiklikleri yapmamız gerekir.
Aslında baştan bu fonksiyonu yazarken, belki de pek önemsenmeyen, yazılım standartlarındaki isimlendirme kurallarına/standartlarına(naming conventions) göre kod yazmaya çalışsak, bu problemlerle karşılaşmaz ve çözümünde de gereksiz zaman harcamazdık.
create or replace function get_count(p_start_date varchar2, p_end_date varchar2) return number is l_count number; begin select count(*) into l_count from T_CHAT_UPDATE_LOOKUP c where c.start_date <= TO_DATE(p_start_date, 'yymmdd') and nvl(c.end_date, to_date('991231', 'yymmdd')) >= TO_DATE(p_end_date, 'yymmdd'); return l_count; end;


Bu vesileyle değişken isimlendirme ile ilgili aşağıdaki bağlantıları da paylaşmak isterim:

Java Kod İsimlendirme ve Şekil (Format) Standardı - http://www.javaturk.org/?p=3180  
Kaliteli Java Kodu Nasıl Yazılır? ya da Kaliteli Kodun Temel İlkeleri - http://www.javaturk.org/?p=3231


ali kemal taşçı

28 Ocak 2015 Çarşamba

SQL Notları: SQL - LIKE Sorgularında Yüzde( percent - '%') ve Alt Çizgi ( underscore - '_' ) Kullanımı

0 yorum
SQL sorgularımızda kullandığımız like ifadesinde “_” , “%” karakterlerini wildcard olarak değil de sorgu karakteri olarak kullanmak istediğimizde bunları escape karakteri ile belirtmeliyiz.

Örneğin, aşağıdaki sorguda “TYPES_” ile başlayan tabloları listelemek istediğimizde, aşağıdaki sorgudaki “_” karakteri wildcard karakteri olarak görüleceğinden istediğimiz sonuç döndürülmeyecektir.

select * from all_tables where table_name like 'TYPES_%';



Bunun için sorgumuzu aşağıdaki şekilde değiştirmeliyiz:

select * from all_tables where table_name like 'TYPES\_%' ESCAPE '\';
 Benzer şekilde içinde “intercon_cdr%rowtype” geçen kodları aramak için de aşağıdaki sorgu istediğimiz sonucu döndürmeyecektir:

select * from dba_SOURCE where lower(text) like '%intercon_cdr%rowtype%'; 


Bunun için sorgumuzu aşağıdaki şekilde değiştirmeliyiz:

select * from dba_SOURCE where lower(text) like '%intercon_cdr\%rowtype%' ESCAPE '\';



Kaynaklar:

ali kemal taşçı