21 Nisan 2015 Salı

Java Collections

http://docs.oracle.com/javase/8/docs/technotes/guides/collections/reference.html
http://www.journaldev.com/1330/java-collections-interview-questions-and-answers
http://en.wikipedia.org/wiki/Java_collections_framework

https://docs.oracle.com/javase/tutorial/collections/intro/index.html
...

Collection birden fazla elemanı birarada tutmaya yarayan bir nesnedir.
Collections frameworkleri collectionların gösterilmesi ve değiştirilmesi için çeşitli interface, implementasyon ve algoritmalar barındırır.

Interfaces


Java Collections Framework'un temelinde core collection interface'leri vardır. Core collection interface'leri bir hiyerarşi içerisinde birçok farklı collection tipini enkapsüle eder. Bu interfaceler sayesinde collectionlar kendi detaylarından bağımsız olarak manipüle edilebilir.


...

Set özel bir Collection türüdür; SortedSet özel bir Set türüdür. Hiyerarşide iki ayrı ağaç bulunur: Map bir Collection değildir.

Bütün core collection interfaceleri generic'tir. Örneğin Collection interface'inin deklarasyonu şöyledir:

public interface Collection...

Buradaki syntax bu interface'in generic olduğunu belirtir.
Bir Collection instance'ı yarattığımızda içinde tutulacak nesne tipini belirtmek zorundayız.
Tip bilgisini belirtmek compiler'ın daha compile-time aşamasında collectiona konulan nesne tiplerinin doğruluğunu kontrol etme imkanı verir.
Bu sayede runtime'da oluşabilecek hataların önüne geçilmiş olur.

Java platformu her collection türünün alt türleri için ayrı ayrı interface'ler tanımlamamıştır. Bunun yerine, her bir interface'de opsiyonel operasyonlar tanımlanmıştır.
Herhangi bir implementasyon bu operasyonları destekleme seçeneğine sahiptir. Dilerse hiçbirini de desteklemeyebilir. Desteklenmeyen bir operasyon çağırılırsa UnsupportedOperationException fırlatılır.

Core collection interface'leri şunlardır:

1- Collection
Collection hiyerarşisinin tepe noktasıdır.
Element adı verilen bit grup nesneyi temsil eder.
Collection interface'i bütün collectionların ortak paydası olduğundan genelde collectionları göndermek ya da genel olarak manipüle etmek için kullanılır.
Bazı collectionlarda aynı elemandan birden fazla bulunabilir, bazılarında bulunamaz.
Bazıları sıralı, bazıları sırasızdır.
Direk implementasyonu yoktur; fakat alt-interface'lerinin direk implementasyonları vardır, örneğin Set ve List gibi.

Collection'a bir örnek olarak:

Collection col = new ArrayList();
col.add("ankara");
col.add(123);
col.add(true);
System.out.println(col);

output:
[a, 1, true]

2- Set
Birden fazla aynı elemanı barındıramaz. Matematikteki küme ifadesinin modelidir.
Bir poker elindeki kağıtlar, bir öğrencinin aldığı dersler, vb.

Set'e örnek olarak:

Set<Integer> tags = new HashSet<>();
tags.add(12);
tags.add(24);
tags.add(48);
tags.add(96);
tags.add(96);
System.out.println(tags);

output:
[48, 96, 24, 12]

3- List
Sıralı bir collectiondır (sequence da denir). Birden fazla aynı elemandan barındırabilir. Kullanıcı index pozisyonlarına göre elemanlara erişebilir.

4- Queue
Bir grup elemanı işlemeden önce elde tutmaya yarar.
Temel Collection operasyonlarının yanında bazı ekstra ekleme, çıkarma, inceleme operasyonları bulunur.
Genellikle elemanları FIFO şeklinde sıralar. Bu kurala uymayan bir örnek priority queue'lardır. Bunlar elemanları verilen bir comparatora göre ya da elemanların doğal sırasına göre sıralar.
Sıralama nasıl olursa olsun, sıranın başındaki eleman ilk olarak atılacaktır (remove ya da poll metodu ile).  FIFO queue'da yeni elemanlar sıranın sonuna eklenir. Diğer queue'larda farklı yerleştirmeler yapılabilir.
Her bir Queue implementasyonu kendi sıralama şeklini belirtmek zorundadır.

5- Deque
İşlem yapmadan önce bir grup elemanı tutmaya yarar. Queue'da olduğu gibi ekstra operasyonları vardır. FIFO ya da LIFO şeklinde kullanılabilir. Yeni elemanlar her iki uca da eklenebilir ya da çıkarılabilir.

6- Map
Bir grup key'i belirli value yani değerlere map'ler, eşleştirme yapar.
Bir mapte aynı key'den birden fazla bulunamaz; bir key birden fazla value'ya map edilemez.

7- SortedSet
Set interface'inin sort edilmiş versiyonudur.
Elemanlar artarak sıralanır.
Genellikle doğası gereği sıralı olan kümeleri, örneğin kelime listeleri ya da üyelik kayıtları gibi grupları modellemekte kullanılır.

8- SortedMap
Map interface'inin sort edilmiş versiyonudur.
Key'ler artarak sıralanır.
Genellikle doğası gereği sıralı olan key-value çiftleri için kullanılır: örneğin sözlük ya da telefon rehberi gibi.


The Collection Interface


Bütün Collection implementasyonlarında argümanı Collection olan bir constructor vardır.
Buna conversion constructor denir. Bu constructor verilen bir collection'ın tipini convert ederek, içindeki elemanlarla yeni bir collection yaratır.

Örneğin List, Set ya da başka tipte bir String collectionı "c" olduğunu farzedelim.
Bu collectiondaki bütün elemanları alıp yeni bir ArrayList yaratmak için:
List list = new ArrayList(c);
...
JDK 7 ve üzeri için diamond operatörü kullanabiliriz:
List list = new ArrayList<>(c);
...

Collection interface'de bazı temel operasyonları gerçekleştiren metodlar bulunur:
 int size(),boolean isEmpty()boolean contains(Object element)boolean add(E element)boolean remove(Object element), Iterator iterator().
...
Bunun yanında bütün bir collection üzerinde yapılan işlemler de vardır:
boolean containsAll(Collection c)boolean addAll(Collection c),boolean removeAll(Collection c)boolean retainAll(Collection c), and void clear().
...

Array işlemleri için bazı metodlar da vardır:
Object[] toArray(),  T[] toArray(T[] a
..

JDK 8 ve üzerinde Collection interface'i collection'larda stream şeklinde nesne alan metodlar bulunur:
 Stream stream() , Stream parallelStream()
...

Collection interface metodları bir collection'daki;
- eleman sayısını gösterir: size , isEmpty
- bir elemanın olup olmadığını gösterir : contains
- eleman ekleme  ve çıkarma işlemlerini yapar : add, remove
- elemanlar üzerinde gezinebilen bir iterator sağlar : iterator

add metodu aynı elemandan birden fazla barındırılmasına müsade eden ve etmeyen tiplere uyması için genel olarak tasarlanmıştır.
add metodu, metod çağrısı tamamlandığında verilen elemanın o collectiona eklendiğini garanti eder. Eğer metod bitiminde collectionda değişiklik olduysa true döndürür.

Aynı şekilde remove metodu da belirtilen elemanın tek bir intance'ının çıkarılmasını sağlar. İşlem sonunda collection değiştiyse true döndürür.

Traversing collections


Bir collectionda gezinmenin üç farklı yolu vardır: aggregate operasyonlar , for-each yapısı ve Iterator'lar.

Aggregate Operations


JDK 8 ve üzerinde bir collectionda gezinmenin tercih edilen yöntemi bir stream almaktır. Bu stream üzerinde aggregate operasyonlar yapılır.
Aggregate operasyonlar genelde lambda ifadeleriyle daha kısa ve öz biçimde yazılır.

Diyelim ki çeşitli isimler içeren bir collection'da sırayla ilerlemek ve listedeki elemanlar içinden sadece erkeklerin isimlerini ekrana yazdırmak istiyoruz .
Bu işlemi stream alarak ve lambda ifadesi kullanarak tek satırda yapabiliriz:

isimCollection.stream()
.filter(e -&gt; e.getGender() == Gender.MALE)
.forEach(e -&gt; System.out.println(e.getName()));
...

Aynı şekilde paralel bir stream alarak çok-çekirdekli mimarinin paralel işlem yapma potansiyelinden de yararlanabiliriz.
isimCollection.parallelStream()
.filter(e -&gt; e.getGender() == Gender.MALE)
.forEach(e -&gt; System.out.println(e.getName()));
...

Bu API ile verileri çok çeşitli yollardan listeleyebiliyoruz.
Diyelim ki bir collection'ın elemanlarını String nesnelerine çevirmek istiyoruz.
Sonra bunları birleştirip aralarına virgül koyacağız.

 String joined = elements.stream()
    .map(Object::toString)
    .collect(Collectors.joining(", "));
...

Diyelim ki bütün çalışanların maaşlarının toplamını almak istiyoruz:

int total = employees.stream()
.collect(Collectors.summingInt(Employee::getSalary)));
...

Aggregate operasyonlar hakkında daha detaylı bilgi için:
https://docs.oracle.com/javase/tutorial/collections/streams/index.html
...


Not: Collections framework API'de bulk operations yani toplu işlemler de vardır.
Bunlar bir collectionın bütünü üzerinde işlem yaparlar, örneğin containsAll, addAll, removeAll, vb.
Bunları aggregate işlemlerle karıştırmamak gerekir.
İkisi arasındaki farkı belirtmek gerekirse: eski yöntem olan bulk operasyonlar mutative yani değişiklik yapan işlemlerdir. Yeni yöntem olan aggregate işlemler ise collectionda hiçbir değişiklik yapmazlar.
Aggregate işlemleri ve lambda ifadelerini kullanırken mutation yani değişiklik yapmaktan kaçınmamız gerekir. Aksi halde ileride paralel bir streamden çalıştırıldığında bu kod sorun yaratacaktır.

for-each yapısı


Bir for loop içerisinde bir collection ya da array üzerinde gezinebiliriz.
Diyelim ki bir collectiondaki tüm objeleri ekrana satır satır yazdırmak için:
for (Object o : collection)
    System.out.println(o);
...

Iterators


Iterator ile bir collectionda gezinebilir ve belirli elemanları seçerek çıkartabiliriz.
Bir collection'ın iterator() metodunu kullanarak o collection üzerinde çalışacak bir Iterator alabiliriz.
Iterator interface'i şöyle tanımlıdır:

public interface Iterator {
    boolean hasNext();
    E next();
    void remove(); //optional
}
...
Buradaki hasNext() metodu, eğer iterasyonda ilerlenebilecek başka elemanlar varsa true döndürür.
next() metodu iterasyonda sıradaki elemanı döndürür.
remove() metodu next() ile döndürülen en son elemanı çıkartır.
remove metodu bir next çağrısında sadece tek bir kez çağırılabilir. Aksi halde exception fırlatır.

Not: Bir collectionı iterasyon sırasında değiştirmek için güvenli olan tek yol Iterator.remove metodunu kullanmaktır.
Aksi halde java.util.ConcurrentModificationException hatası alınabilir.
Collection'daki remove() metodu fail-fast bir metoddur.
Yani hataya sebep olabilecek durumlarda hemen hataya düşerek ileride sorun çıkmasının önünü almak ister.
Örneğin aşağıdaki kod exception verecektir:

Iterator iter = list.iterator();
        while (iter.hasNext()) {
            String s = (String) iter.next();
            if (s.equals("hello")) {
                list.remove(s);
            }
        }
...
Bunu düzeltmek için List.remove yerine Iterator.remove metodunu kullanmak gerekir. Şimdi hata vermeyecektir:

while (iter.hasNext()) {
            String s = (String) iter.next();
            if (s.equals("hello")) {
                iter.remove();
            }
        }

...


Hangi  durumlarda for-each yerine Iterator tercih edilmelidir?

- Current element silinecekse.
For-each yöntemi iteratorı gizlediğinden remove metodunu çağıramayız. Bu yüzden for-each yapısı filtreleme için uygun değildir.
- Paralel olarak birden fazla collectionda iterasyon yapılması gerekiyorsa.


Bir collectionı filtreleyerek belirli elemanları silmek için şu yöntem uygulanır:
static void filter(Collection c) {
    for (Iterator it = c.iterator(); it.hasNext(); )
        if (!cond(it.next()))
            it.remove();
}
...
Burada polymorphic bir metod yazılmıştır. Hangi collection implementasyonu olursa olsun, hepsinde çalışacaktır.
Java Collections Framework ile bu şekilde polymorphic algoritmaları kolayca yazabiliriz.

Collection Interface Bulk Operations


Bulk yani toplu işlemler bir collectionın bütünü üzerinden yapılır.
-containsAll : verilen collectiondaki tüm elemanlar hedefte bulunuyorsa true döndürür
-addAll         : verilen collectiondaki bütün elemanları hedefe ekler
-removeAll   : hedefteki collectiondan verilen collectiondaki tüm elemanları siler
-retainAll      : hedefteki collectiondan verilen collectionda olmayan tüm elemanları siler
-clear            : verilen collectiondaki bütün elemanları siler

addAll, removeAll ve retainAll metodları eğer hedefte değişiklik olmuşsa true döndürürler.

Diyelim ki bir collectiondan belirli bir e elemanının bütün instance'larını silmek istiyoruz.

c.removeAll(Collections.singleton(e));

Ya da bütün null elemanları silelim:

c.removeAll(Collections.singleton(null));

Burada Collections.singleton static factory metodu kullanılmış. Bu metod immutable bir Set döndürür; bu sette sadece belirtilen eleman bulunur.

Collections Interface Array Operations

Collectionlar ile parametre olarak eski tip array isteyen eski API'ler arasında geçiş yapabilmek için toArray metodları kullanılır.
Bu metodlar bir collectiondaki bütün elemanları bir array içinde toplar.
Argüman verilemzse yeni bir Object arrayi yaratır.
Diğer versiyonlarında bir array verebilir ya da arrayin runtime tipini belirtebiliriz.

Diyelim ki elimizde bir c Collectionı olsun. c'nin bütün elemanlarını yeni bir Object arrayine atalım.

Object[] a = c.toArray();

Diyelim ki c sadece String lerden oluşsun. c'nin elemanlarını bir String array'ine aktaralım.

String[] a = c.toArray(new String[0]);


1 yorum: