• Tidak ada hasil yang ditemukan

Java Collection Framework

Dalam dokumen c2ed0 java desktop ifnu bima (Halaman 77-84)

Membuat aplikasi yang cukup kompleks tanpa adanya collection yang baik akan sangat repot, banyak hal harus dilakukan hanya untuk membuat collection yang memenuhi kebutuhan kita. Untungnya dalam JDK terdapat banyak sekali collection yang sudah tersedia, kita hanya perlu mempelajari API tersebut dan memahami kapan saat terbaik untuk menggunakan collection API tersebut.

Dalam Java collection API ada beberapa interface kunci yang merupakan dasar dari collection yang ada dalam JDK. Interface-interface itu antara lain :

Collection Set SortedSet

List Map SortedMap

Queue NavigableSet NavigableMap

Dalam buku ini kita akan membahas beberapa interface saja yang sering dibutuhkan dalam aplikasi, antara lain: List, Set, Map dan Queue. Daftar class yang mengimplementasikan keempat interface di atas antara lain :

Map Set List Queue

HashMap HashSet ArrayList PriorityQueue

HashTable LinkedHashSet Vector

TreeMap TreeSet LinkedList

LinkedHashMap

Setiap jenis collection di atas mempunyai empat varian : terurut (sorted), tidak terurut (unsorted), teratur (ordered) dan tidak teratur (unordered). Collection yang terurut (sorted) artinya isi dari collection tersebut terurut berdasarkan nilai tertentu dari objectnya, misalnya kalau yang dimasukkan dalam collection adalah object dari Customer, kita bisa mengurutkan berdasarkan id atau berdasarkan nilai property yang lain. Kita akan membahas lebih lanjut bagaimana caranya melakukan sorting atau menggunakan sorted collection di bab selanjutnya. Collection tidak terurut jelas kebalikan dari collection terurut.

Collection teratur (ordered) adalah collection yang diatur berdasarkan kapan item tersebut dimasukkan ke dalam collection, item yang dimasukkan pertama akan berada di awal, sedangkan item yang dimasukkan terakhir akan berada di akhir collection. Sedangkan collection tidak teratur (unordered) jelas kebalikan dari collection teratur.

Nah mari kita bahas satu per satu dari keempat collection di atas beserta varianya, dimulai dari List.

List

List adalah jenis collection yang teratur tetapi tidak terurut. List mempunyai index yang disusun berdasarkan urutan kapan item dimasukkan ke dalam List. Isi dari List bersifat tidak unik, alias dua buah item yang sama bisa dimasukkan berkali kali ke dalam List. Method penting dalam List antara lain :

• get(int index); method ini digunakan untuk mengambil isi dari list berdasarkan index (urutan item dimasukkan ke dalam List)

• indexOf(Object o); method ini digunakan untuk mengetahui berapa nomor index dari object yang ada dalam List.

• add(Object o); digunakan untuk menambahkan object ke dalam List

Class ArrayList adalah class yang mengimplementasikan interface List, bayangkan class ArrayList ini adalah Array yang dapat bertambah ukuranya. Vector adalah class yang juga mengimplementasikan List, Vector adalah pendahulu dari ArrayList, kalau tidak ada yang menghalangi anda menggunakan ArrayList, sebaiknya hindari Vector karena performance ArrayList lebih bagus dan classnya lebih modern.

LinkedList adalah implementasi dari List yang menambahkan method baru untuk menambahkan atau menghapus isi dari List dari depan atau dari belakang. Class ini cocok digunakan untuk membuat tumpukan (stack) atau antrian (queue).

Mari kita lihat contoh penggunaan List dan ArrayList dalam kode di bawah ini : public class ListTest {

public static void main(String[] args) { List<String> list = new ArrayList<String>(); list.add("a");

list.add("b"); list.add("c"); list.add("a"); list.add("z");

System.out.println("isi dari list : "); for(int i=0; i< list.size();i++) {

System.out.println("index ke-" + i + ":" + list.get(i) ); }

} }

Kode di atas diawali dengan deklarasi variabel List yang menggunakan generics : List<String>, kode ini artinya kita akan membuat variabel dengan tipe List dan isinya hanya String saja, tipe data lain tidak bisa dimasukkan ke dalam List ini.

Hasil eksekusi kode di atas adalah seperti di bawah ini : $ javac ListTest.java

$ java ListTest isi dari list : index ke-0:a index ke-1:b index ke-2:c index ke-3:a index ke-4:z $

secara visual isi dari list di atas bisa digambarkan dalam diagram seperti di bawah ini :

terlihat bahwa index adalah bagian terpenting dari List, sedangkan item dalam List sendiri bisa dobel, tidak unik. Selain itu item di dalam List juga tidak terurut, terlihat dari huruf “a” yang diletakkan setelah huruf “c”.

Set

Set adalah collection yang bersifat unik. Set digunakan kalau anda memerlukan collection yang isinya harus unik. Defnisi unik diimplementasikan dengan mengoverride method equals dan hashCode dari class yang akan dimasukkan ke dalam Set. Semua class wrapper seperti String telah mengoverride method equals dan hashCode sehingga dua buah object String dianggap sama kalau string yang ada di dalamnya sama. Untuk class yang kita buat sendiri, seperti class

0

“a”

1

“b”

2

“c”

3

“a”

4

“z”

index

item

Customer, maka method equals dan hashCode harus dioverride dahulu sebelum dimasukkan ke dalam Set. Kalau method equals dan hashCode tidak dioverride maka dua buah object dianggap sama kalau keduanya merefer ke object yang sama di dalam memory, tidak seperti yang kita harapkan.

Class implementasi dari interface Set yang paling sering digunakan adalah HashSet. Nilai yang dikembalikan oleh hashCode sangat penting dalam class HashSet. Nilai hashCode digunakan untuk menentukan di mana object tersebut diletakkan dalam “keranjang” HashSet. Ketika method add di panggil dengan parameter object, HashSet akan memaggil method hashCode dari object tersebut untuk menentukan di keranjang mana object akan diletakkan. Setelah ketemu nomer keranjangnya, dicek apakah keranjang tersebut kosong. Kalau kosong maka object langsung diletakkkan di keranjang tersebut. Kalau keranjang ada isinya, dilakukan pengetesan menggunakan method equals, kalau ada object di dalam keranjang ditest menggunakan method equals menghasilkan true, maka object lama akan ditimpa dengan object baru yang dimasukkan ke dalam HashSet menggunakan method add tadi.

Kecepatan proses pencarian object di dalam HashSet tergantung dari bagaimana kita melalukan perhitungan hashCode. Kalau misalnya kita selalu return nilai 1 untuk semua object, maka semua object akan diletakkan dalam satu keranjang yang sama, proses pencarian menjadi linier (O(n)) karena object akan dibandingkan satu per satu menggunakan method equals. Kalau hashCode bersifat unik untuk setiap object, maka proses pencarianya sangat cepat yaitu O(1), HashSet akan memanggil method hashCode untuk mendapatkan nilainya. Kalau nilai hashCode tidak selalu unik, HashSet akan melihat di dalam keranjang tersebut adakah object di dalamnya, kalau ada satu buah maka object itu akan dikembalikan sebagai hasil pencarian, kalau kosong yang dikembalikan adalah null, kalau ada beberapa buah maka untuk setiap object di dalam keranjang ditest menggunakan method equals, ketika method equals mengembalikan true maka object tersebut adalah hasil pencarianya.

Jika dua buah object dipanggil method equals menghasilkan nilai true tetapi hashCodenya berbeda (menyalahi aturan), maka oleh HashSet kedua object ini dianggap berbeda karena akan diletakkan di dalam keranjang yang berbeda, asal keranjangnya berbeda maka method equals akan diabaikan, tidak pernah dipanggil. Jadi harus ditaati aturan bahwa kalau dua buah object dioperasikan dengan method equals mengembalikan true, nilai hashCodenya harus sama.

Perhatikan visualisasi HashSet yang berisi String di bawah ini :

Algoritma sederhana untuk menghitung hashCode dari class String adalah menambahkan urutan dari hurufnya, misalnya string “a” nilai hashCodenya 1, “b” nilai hashCodenya 2, sedangkan string “ab” dan “ba” nilai hashCodenya sama yaitu 3. Bisa kita lihat juga string “ab” dan “ba” diletakkan dalam keranjang yang sama karena hashCodenya sama, tetapi tidak saling timpa, hal ini karena “ab”.equals(“ba”) bernilai false.

Tujuh paragraf menerangkan tentang Set kok rasanya masih kurang kalau kalau belum melihat kode bagaimana menggunakan Set ini. Kita akan menggunakan class Customer yang sudah dioverride method equals dan hashCodenya di bab sebelumnya, perhatikan juga bahwa equals dan hashCode dari class Customer di atas , kemudian buat class di bawah ini di folder yang sama dengan class Customer :

import java.util.Set; import java.util.HashSet; import java.util.Iterator;

3

2

1

“a” “b” “ab”

“ba”

keranjang

public class CustomerSetTest{

public static void main(String[] args) {

Set<Customer> customers = new HashSet<Customer>(); Customer id1 = new Customer();

id1.setId(1l); customers.add(id1);

Customer id2 = new Customer(); id2.setId(2l);

customers.add(id2);

Customer c = new Customer(); c.setId(1l);

customers.add(c); //mereplace id1 karena mempunyai id yang sama Iterator<Customer> i = customers.getIterator();

while(i.hasNext()){

Customer current = i.next();

System.out.println("keranjang no-" + current.hashCode() + " idnya:" + current.getId());

} } }

Perhatikan juga kode di atas menggunakan Iterator untuk mengambil nilai dalam Set satu per satu, ada bentuk for each yang diperkenalkan sebagai bagian dari Java 5 Language enhancement, bentuk for each ini menyederhanakan kode untuk mengakses item di dalam Set. For each akan kita bahas di bab tentang java 5 languge enhancement. Hasil eksekusi kode di atas adalah seperti di bawah ini :

$ javac Customer.java CustomerSetTest.java Customer.java $ java CustomerSetTest

keranjang no-373 idnya:2 keranjang no-372 idnya:1 $

Anda mungkin juga terkejut, ternyata nomor keranjangnya pun tidak terurut dari kecil ke besar. HashSet adalah collection yang hanya menekankan pada sifat unik saja, sedangkan urutan dan keteraturan tidak dijamin sama sekali. Ada jenis collection lain yang bisa menjamin sifat unik sekaligus terurut, yaitu TreeSet, kita akan membahas tentang TreeSet nanti di bab setelah kita bahas konsep sorting.

Map

Map adalah bentuk struktur data yang berpasangan antara key-value. Key bersifat unik karena implementasinya menggunakan Set. Key dan value adalah object, bisa object apa saja, tipe data primitif tidak bisa dimasukkan ke dalam Map, juga tidak bisa dimasukkan ke semua jenis collection.

Implementasi Map memberikan kemampuan untuk mencari value berdasarkan key, setiap kali kita dihadapkan pada permasalahan bagaimana caranya melakukan pencarian berdasarkan value kita bisa menggunakan Map ini. Map bergantung pada method equals dan hashCode untuk menentukan apakah dua buah key sama atau tidak, jadi kalau kita ingin menggunakan object dari class yang kita buat sendiri sebagai key, maka kedua method itu harus dioverride untuk menentukan bagaimana dua buah object dianggap sama atau tidak.

HashMap adalah class yang mengimplementasikan interface Map, sifatnya adalah tidak terurut dan tidak teratur. Implementasi HashMap adalah yang paling efsien, implementasi Map lainya yang membutuhkan keterurutan atau keteraturan akan menambahkan komputasi tambahan sehingga kurang efsien dibanding dengan HashMap. HashMap memperbolehkan satu buah key dengan nilai null dan banyak key bukan null yang valuenya adalah null.

Method penting yang dimiliki oleh Map antara lain :

• put(Object key, Object value); method yang digunakan untuk meletakkan pasangan key dan value

• get(Object key); method digunakan untuk mendapatkan value dari key yang dimasukkan sebagai parameter

• keySet(); digunakan untuk mendapatkan Set yang berisi semua key, biasanya kita ingin mendapatkan Set dari key ini kalau ingin mengambil nilai dalam Map satu per satu

Mari kita lihat contoh kode penggunaan Map. Kita akan membuat sebuah map dimana key adalah kota dan value adalah kumpulan Customer yang tinggal di kota tersebut. Tipe data key adalah String dan tipe data valuenya adalah List of Customer:

import java.util.Map; import java.util.HashMap; import java.util.Set; import java.util.Iterator; import java.util.List; import java.util.ArrayList; public class MapTest {

public static void main(String[] args) {

Map<String,List<Customer>> customerByCityMap = new HashMap<String,List<Customer>>();

List<Customer> jakartaCust = new ArrayList<Customer>(); Customer a = new Customer();

a.setId(1l);

jakartaCust.add(a);

Customer b = new Customer(); b.setId(2l);

jakartaCust.add(b);

customerByCityMap.put("jakarta",jakartaCust);

List<Customer> surabayaCust = new ArrayList<Customer>(); Customer c = new Customer();

c.setId(3l);

surabayaCust.add(c);

customerByCityMap.put("surabaya",surabayaCust); Set<String> keys = customerByCityMap.keySet(); Iterator<String> iterator = keys.iterator(); while(iterator.hasNext()) {

String key = iterator.next();

List<Customer> customers = customerByCityMap.get(key); for(int i = 0;i < customers.size(); i++) {

Customer cust = customers.get(i);

System.out.println("kota : " + key + ", Customer id : " + cust.getId());

} } } }

hasil eksekusi kode di atas adalah : $ javac MapTest.java Customer.java $ java MapTest

kota : jakarta, Customer id : 1 kota : jakarta, Customer id : 2 kota : surabaya, Customer id : 3 $

Ada satu class lagi yang mengimplement Map interface sekaligus keynya diurutkan berdasarkan logic tertentu, yaitu class TreeSet. Kita akan membahas class TreeSet sekaligus HashSet setelah membahas bab Sorting di bawah ini.

Sorting

Sorting adalah cara untuk membuat sebuah collection terurut (sorted). Agar collection terurut kita perlu membuat item yang akan dimasukkan ke dalam Set mengimplementasikan interface Comparable. Interface ini digunakan untuk melakukan perbandingan antara dua buah objec, mana yang lebih besar atau lebih kecil ataukah sama persis.

Interface Comparable hanya mempunyai satu buah method, yaitu compareTo: method ini mempunyai sebuah parameter yang bertipe Object kalau tidak menggunakan generics, dan bertipe sama dengan class yang mengimplement interface Comparable kalau menggunakan generics. Method compareTo mengembalikan integer, nilai kembalianya positif berarti object yang dipanggil method comparenya lebih besar dari object yang dimasukkan ke parameter, nilai kembalianya negatif berarti sebaliknya dan kalau nilainya nol berarti kedua object sama besar. Kita lihat contoh implementasi interface Comparable di class Customer. Pertama, kita akan mengimplementasikan interface Comparable tanpa menggunakan generics :

import java.lang.Comparable;

public class Customer implements Comparable{ private Long id;

public void setId(Long aId){ this.id = aId;

}

public Long getId() { return this.id; }

public int compareTo(Object o) { Customer c = (Customer) o;

return getId().compareTo(c.getId()); }

public boolean equals(Object obj) { if (obj == null) { return false; } if (getClass() != obj.getClass()) { return false; }

final Customer other = (Customer) obj;

if (this.id != other.id && (this.id == null || !this.id.equals(other.id))) { return false;

}

return true; }

public int hashCode() { int hash = 7;

hash = 53 * hash + (this.id != null ? this.id.hashCode() : 0); return hash;

} }

Kalau menggunakan generics maka method compareTo tipe parameternya adalah Customer, bukan Object. Contohnya :

import java.lang.Comparable;

public class Customer implements Comparable<Customer>{ private Long id;

public void setId(Long aId){ this.id = aId;

}

public Long getId() { return this.id;

}

public int compareTo(Customer c) { return getId().compareTo(c.getId()); }

public boolean equals(Object obj) { if (obj == null) { return false; } if (getClass() != obj.getClass()) { return false; }

final Customer other = (Customer) obj;

if (this.id != other.id && (this.id == null || !this.id.equals(other.id))) { return false;

}

return true; }

public int hashCode() { int hash = 7;

hash = 53 * hash + (this.id != null ? this.id.hashCode() : 0); return hash;

} }

Perbandingan dalam method compareTo di atas akan menyebabkan Customer akan diurutkan berdasarkan idnya dari yang kecil ke besar. Kalau ingin diurutkan dari besar ke kecil, maka perbandinganya tinggal dibalik seperti di bawah ini :

public int compareTo(Customer c) { return c.getId().compareTo(getId()); }

Interface Comparable bisa digunakan kalau source code dari class Customer tersedia dan kita bisa melakukan modifkasi terhadap class Customer. Kalau misalnya class yang ingin diurutkan tidak tersedia source codenya, kita bisa membuat class baru kemudian mengextends class tersebut sekaligus mengimplementasikan interface Comparable, sehingga kita bisa membuat collection terurut dari class baru ini. Nah, bagaimana kalau classnya ditandai dengan fnal? Kita tidak mungkin mengextends class tersebut, ada cara kedua, yaitu dengan membuat class baru yang mengimplementasikan interface Comparator.

Interface Comparator mempunyai bentuk generics dan bentuk tanpa generics, seperti halnya interface Comparable, bentuk tanpa generics akan menyebabkan parameter bertipe Object sehingga perlu proses casting untuk mengubah menjadi Customer, sedangkan bentuk generics tipe data parameternya sudah bertipe Customer. mari kita lihat bagaimana cara membuat class yang mengimplementasikan interface Comparator dengan bentuk generics, bentuk non generics saya serahkan kepada anda untuk membuat sendiri:

import java.util.Comparator;

public class CustomerComparator implements Comparator<Customer>{ public int compare(Customer c1, Customer c2) {

return c1.getId().compareTo(c2.getId()); }

}

Nah, setelah Customer mengimplementasikan interface Comparable, kita bisa menggunakan collection yang sifatnya terurut, misalnya TreeSet dan TreeMap. Kita ubah sedikit kode contoh penggunaan Set dengan mengganti HashSet menjadi TreeSet, seperti di bawah ini :

import java.util.Set; import java.util.TreeSet; import java.util.Iterator;

public static void main(String[] args) {

Set<Customer> customers = new TreeSet<Customer>(); Customer id1 = new Customer();

id1.setId(1l); customers.add(id1);

Customer id2 = new Customer(); id2.setId(2l);

customers.add(id2);

Customer c = new Customer(); c.setId(1l);

customers.add(c); //mereplace id1 karena mempunyai id yang sama Iterator<Customer> i = customers.getIterator();

while(i.hasNext()){

Customer current = i.next();

System.out.println("keranjang no-" + current.hashCode() + " idnya:" + current.getId());

} } }

kalau kita eksekusi kode di atas maka customer akan diurutkan berdasarkan idnya, seperti di bawah ini :

$ javac CustomerTreeSetTest.java Customer.java $ java CustomerTreeSetTest

keranjang no-372 idnya:1 keranjang no-373 idnya:2 $

TreeSet dan TreeMap menyaratkan item yang dimasukkan mengimplementasikan Comparable, atau kalau itemnya tidak bisa mengimplementasikan Comparable, kita harus menyediakan class yang mengimplementasikan Comparator dan memasukkan object dari class Comparator tersebut di constructor dari TreeSet atau TreeMap, seperti cuplikan kode di bawah ini :

Set<Customer> customers = new TreeSet<Customer>(new CustomerComparator()); Map<Customer> customerMap = new TreeMap<Customer>(new CustomerComparator());

Kedua collection ini akan menyusun item dalam keadaan terurut menggunakan struktur data tree, kalau item yang dimasukkan lebih kecil dari root maka diletakkan di tree sebelah kiri, kalau lebih besar diletakkan di sebelah kanan. Algoritma seperti ini menyebabkan proses penambahan item baru ke dalam collection menjadi sedikit lebih lambat daripada HashSet. Tetapi ketika isi dari TreeSet diambil, keadaanya sudah dalam kondisi terurut, sehingga tidak diperlukan proses pengurutan lagi.

Jenis collection yang bersifat terurut hanya tersedia untuk Set dan Map, sedangkan untuk List tidak ada. Nah kalau kita ingin membuat sebuah List terurut kita bisa menggunakan class Collections yang di dalamnya terdapat method yang bisa mengurutkan List. Kita akan membahas tentang class Collections di bab selanjutnya.

Dalam dokumen c2ed0 java desktop ifnu bima (Halaman 77-84)