1. Pengenalan Collections Framework
Java Collections Framework adalah sekumpulan class dan interface yang menyediakan struktur data siap pakai untuk menyimpan dan memanipulasi kelompok objek. Framework ini berada di package java.util dan merupakan salah satu fitur paling penting dalam Java.
Sebelum Collections Framework, Java menggunakan class seperti Vector, Hashtable, dan Array yang terpisah-pisah dan tidak konsisten. Collections Framework menyatukan semuanya dalam satu arsitektur yang terstruktur.
Mengapa Collections Framework Penting?
| Keunggulan | Penjelasan |
|---|---|
| Struktur Data Siap Pakai | Tidak perlu membuat linked list, hash table, tree dari nol |
| Generics | Type-safe β tipe data dicek saat compile, bukan runtime |
| Algoritma Siap Pakai | Sorting, searching, shuffling tersedia via Collections utility |
| Konsistensi API | Semua collection punya interface seragam (add, remove, size, dll) |
| High Performance | Diimplementasikan dengan algoritma optimal oleh tim expert Java |
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β COLLECTIONS HIERARCHY β β β β βββββββββββββ β β βIterable β β β βββββββ¬ββββββ β β βββββββ΄ββββββ β β βCollection β β β βββββββ΄βββ¬βββββββββ΄βββ¬ββββββββ β β βΌ βΌ βΌ βΌ β β ββββββββ ββββββββ ββββββββ βββββββββ β β β List β β Set β β Queueβ βDeque β β β ββββββββ ββββββββ ββββββββ βββββββββ β β β β βββββββββββββ β β β Map β (terpisah dari Collection) β β βββββββ΄βββ¬βββββββββ΄βββ β β βΌ βΌ βΌ β β ββββββββββββββββββββββββββββββ β β βHashMap ββTreeMap ββLinked β β β β ββ ββHashMap β β β ββββββββββββββββββββββββββββββ β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
2. List β ArrayList dan LinkedList
List adalah collection yang menyimpan elemen secara berurutan (ordered) dan mengizinkan duplikat. Dua implementasi utama: ArrayList (berbasis array, cepat untuk akses random) dan LinkedList (berbasis linked list, cepat untuk insert/delete).
ArrayList
import java.util.ArrayList;
import java.util.List;
public class ArrayListDemo {
public static void main(String[] args) {
// Membuat ArrayList dengan Generics (type-safe)
List<String> buah = new ArrayList<>();
// Menambah elemen
buah.add("Apel");
buah.add("Mangga");
buah.add("Jeruk");
buah.add("Pisang");
buah.add("Apel"); // duplikat diperbolehkan
System.out.println("Daftar buah: " + buah);
// [Apel, Mangga, Jeruk, Pisang, Apel]
// Akses elemen berdasarkan index
System.out.println("Buah pertama: " + buah.get(0)); // Apel
System.out.println("Jumlah: " + buah.size()); // 5
// Mengubah elemen
buah.set(1, "Durian");
System.out.println("Setelah diubah: " + buah);
// Menghapus elemen
buah.remove("Jeruk"); // berdasarkan objek
buah.remove(0); // berdasarkan index
System.out.println("Setelah hapus: " + buah);
// Mengecek keberadaan elemen
System.out.println("Ada Mangga? " + buah.contains("Durian")); // true
System.out.println("Index Durian: " + buah.indexOf("Durian")); // 0
// Loop dengan for-each
for (String b : buah) {
System.out.println("- " + b);
}
// Loop dengan index
for (int i = 0; i < buah.size(); i++) {
System.out.println(i + ": " + buah.get(i));
}
// ArrayList dengan tipe data lain
List<Integer> angka = new ArrayList<>();
angka.add(10);
angka.add(20);
angka.add(30);
System.out.println("Angka: " + angka);
// Inisialisasi langsung
List<String> warna = new ArrayList<>(List.of("Merah", "Hijau", "Biru"));
System.out.println("Warna: " + warna);
}
}
LinkedList
import java.util.LinkedList;
public class LinkedListDemo {
public static void main(String[] args) {
LinkedList<String> antrian = new LinkedList<>();
// Sebagai Queue (FIFO β First In First Out)
antrian.add("Budi"); // tambah di belakang
antrian.add("Ani");
antrian.add("Citra");
System.out.println("Antrian: " + antrian);
System.out.println("Dilayani: " + antrian.poll()); // Budi (ambil & hapus)
System.out.println("Lihat depan: " + antrian.peek()); // Ani (lihat saja)
// Sebagai Deque (Double-Ended Queue)
antrian.addFirst("Dina"); // tambah di depan
antrian.addLast("Eko"); // tambah di belakang
System.out.println("Deque: " + antrian);
System.out.println("Depan: " + antrian.getFirst());
System.out.println("Belakang: " + antrian.getLast());
}
}
Kapan Menggunakan Mana?
| Operasi | ArrayList | LinkedList |
|---|---|---|
| Get(index) | π’ O(1) β Cepat | π΄ O(n) β Lambat |
| Add di akhir | π’ O(1) amortized | π’ O(1) |
| Add di tengah | π΄ O(n) | π’ O(1) |
| Remove | π΄ O(n) | π’ O(1) |
| Memory | π’ Compact | π΄ Lebih besar (pointer) |
| Cocok untuk | Baca random, iterasi | Insert/hapus sering |
3. Set β HashSet, TreeSet, LinkedHashSet
Set adalah collection yang tidak mengizinkan duplikat. Set cocok digunakan untuk menyimpan data unik seperti daftar username, ID, atau kumpulan tag.
import java.util.*;
public class SetDemo {
public static void main(String[] args) {
// ===== HASHSET β Tidak berurutan, performa cepat =====
Set<String> kota = new HashSet<>();
kota.add("Jakarta");
kota.add("Bandung");
kota.add("Surabaya");
kota.add("Jakarta"); // duplikat β akan diabaikan!
kota.add("Yogyakarta");
System.out.println("Kota: " + kota);
// Output: [Bandung, Yogyakarta, Surabaya, Jakarta] β urutan acak
System.out.println("Jumlah kota: " + kota.size()); // 4, bukan 5
// Cek keberadaan β O(1) cepat!
System.out.println("Ada Jakarta? " + kota.contains("Jakarta")); // true
// Hapus elemen
kota.remove("Bandung");
// Loop
for (String k : kota) {
System.out.println("- " + k);
}
// ===== TREESET β Terurut ascending =====
Set<Integer> angka = new TreeSet<>();
angka.add(50);
angka.add(10);
angka.add(30);
angka.add(10); // duplikat β diabaikan
angka.add(20);
System.out.println("TreeSet (terurut): " + angka);
// Output: [10, 20, 30, 50] β selalu terurut!
// Operasi Set
Set<String> setA = new HashSet<>(Set.of("A", "B", "C", "D"));
Set<String> setB = new HashSet<>(Set.of("C", "D", "E", "F"));
// Union (gabungan)
Set<String> union = new HashSet<>(setA);
union.addAll(setB);
System.out.println("Union: " + union); // [A, B, C, D, E, F]
// Intersection (irisan)
Set<String> intersection = new HashSet<>(setA);
intersection.retainAll(setB);
System.out.println("Intersection: " + intersection); // [C, D]
// Difference (selisih)
Set<String> difference = new HashSet<>(setA);
difference.removeAll(setB);
System.out.println("Difference: " + difference); // [A, B]
}
}
Perbandingan Implementasi Set
| Implementasi | Urutan | Kecepatan | Cocok untuk |
|---|---|---|---|
| HashSet | Tidak terurut | π’ Tercepat | Set umum, lookup cepat |
| TreeSet | Terurut ascending | π‘ Sedang | Data perlu urutan |
| LinkedHashSet | Sesuai urutan insert | π‘ Sedang | Perlu urutan masuk |
4. Map β HashMap, TreeMap, LinkedHashMap
Map menyimpan data dalam pasangan key-value. Setiap key harus unik, tapi value boleh duplikat. Map bukan bagian dari Collection interface tapi sangat penting.
import java.util.*;
public class MapDemo {
public static void main(String[] args) {
// Membuat HashMap
Map<String, Integer> nilaiMahasiswa = new HashMap<>();
// Menambah pasangan key-value
nilaiMahasiswa.put("Budi", 85);
nilaiMahasiswa.put("Ani", 92);
nilaiMahasiswa.put("Citra", 78);
nilaiMahasiswa.put("Dina", 95);
System.out.println("Data: " + nilaiMahasiswa);
// Mengambil value berdasarkan key
System.out.println("Nilai Budi: " + nilaiMahasiswa.get("Budi")); // 85
// Key tidak ada β default value
System.out.println("Nilai Eko: " + nilaiMahasiswa.getOrDefault("Eko", 0)); // 0
// Cek keberadaan key
System.out.println("Ada Ani? " + nilaiMahasiswa.containsKey("Ani")); // true
System.out.println("Ada nilai 95? " + nilaiMahasiswa.containsValue(95)); // true
// Mengubah value
nilaiMahasiswa.put("Budi", 88); // update nilai Budi
System.out.println("Budi baru: " + nilaiMahasiswa.get("Budi")); // 88
// Hapus
nilaiMahasiswa.remove("Citra");
// Iterasi Map
System.out.println("\n=== Iterasi Entry Set ===");
for (Map.Entry<String, Integer> entry : nilaiMahasiswa.entrySet()) {
System.out.println(entry.getKey() + ": " + entry.getValue());
}
// Iterasi keys saja
System.out.println("\n=== Semua Nama ===");
for (String nama : nilaiMahasiswa.keySet()) {
System.out.println("- " + nama);
}
// Iterasi values saja
System.out.println("\n=== Semua Nilai ===");
int total = 0;
for (int nilai : nilaiMahasiswa.values()) {
total += nilai;
}
System.out.println("Total: " + total);
System.out.println("Rata-rata: " + (total / nilaiMahasiswa.size()));
// ===== TREEMAP β Key terurut =====
Map<String, String> ibuKota = new TreeMap<>();
ibuKota.put("Indonesia", "Jakarta");
ibuKota.put("Jepang", "Tokyo");
ibuKota.put("Amerika", "Washington D.C.");
ibuKota.put("Inggris", "London");
System.out.println("\nTreeMap (terurut): " + ibuKota);
// Selalu terurut berdasarkan key!
// Merge β gabungkan value untuk key yang sama
Map<String, Integer> stok = new HashMap<>();
stok.put("Apel", 10);
stok.merge("Apel", 5, Integer::sum); // tambah 5
stok.merge("Mangga", 8, Integer::sum); // baru
System.out.println("\nStok: " + stok); // {Apel=15, Mangga=8}
// computeIfAbsent β lazy initialization
Map<String, List<String>> kategori = new HashMap<>();
kategori.computeIfAbsent("Buah", k -> new ArrayList<>()).add("Apel");
kategori.computeIfAbsent("Buah", k -> new ArrayList<>()).add("Mangga");
kategori.computeIfAbsent("Sayur", k -> new ArrayList<>()).add("Bayam");
System.out.println("Kategori: " + kategori);
// {Buah=[Apel, Mangga], Sayur=[Bayam]}
}
}
5. Iterator dan Iterable
Iterator adalah objek yang memungkinkan kita menelusuri (traverse) elemen collection satu per satu. Semua collection di Java mengimplementasi Iterable sehingga bisa digunakan dalam for-each loop.
import java.util.*;
public class IteratorDemo {
public static void main(String[] args) {
List<String> bahasa = new ArrayList<>(
List.of("Java", "Python", "JavaScript", "Go", "Rust")
);
// ===== Menggunakan Iterator secara eksplisit =====
System.out.println("=== Iterator Manual ===");
Iterator<String> it = bahasa.iterator();
while (it.hasNext()) {
String b = it.next();
System.out.println("- " + b);
// Hapus elemen yang mengandung 'a' (huruf kecil)
if (b.toLowerCase().contains("a")) {
it.remove(); // AMAN β tidak ConcurrentModificationException
}
}
System.out.println("Sisa: " + bahasa); // [Go]
// ===== For-each loop (syntactic sugar untuk Iterator) =====
bahasa = new ArrayList<>(List.of("Java", "Python", "Go", "Rust"));
System.out.println("\n=== For-Each Loop ===");
for (String b : bahasa) {
System.out.println("- " + b);
}
// ===== ListIterator β bisa mundur dan tambah/hapus =====
System.out.println("\n=== ListIterator ===");
ListIterator<String> lit = bahasa.listIterator();
while (lit.hasNext()) {
String b = lit.next();
if (b.equals("Go")) {
lit.set("Golang"); // mengganti elemen
}
}
System.out.println("Setelah modifikasi: " + bahasa);
// Iterasi mundur
System.out.println("\n=== Iterasi Mundur ===");
ListIterator<String> litBack = bahasa.listIterator(bahasa.size());
while (litBack.hasPrevious()) {
System.out.println("- " + litBack.previous());
}
// ===== forEach dengan Lambda (Java 8+) =====
System.out.println("\n=== forEach Lambda ===");
bahasa.forEach(b -> System.out.println(" " + b));
// ===== Iterator pada Map =====
Map<String, Integer> skor = new HashMap<>();
skor.put("Budi", 85);
skor.put("Ani", 92);
skor.put("Citra", 78);
// Iterator via entrySet
Iterator<Map.Entry<String, Integer>> mapIt = skor.entrySet().iterator();
while (mapIt.hasNext()) {
Map.Entry<String, Integer> entry = mapIt.next();
System.out.println(entry.getKey() + " = " + entry.getValue());
}
}
}
Jangan menghapus elemen collection langsung di dalam for-each loop menggunakan collection.remove() karena akan menyebabkan ConcurrentModificationException. Gunakan Iterator.remove() atau removeIf() untuk menghapus elemen dengan aman.
6. Comparable dan Comparator
Untuk mengurutkan objek dalam collection, Java menggunakan dua interface: Comparable (natural ordering β definisi dari dalam class) dan Comparator (custom ordering β definisi dari luar class).
import java.util.*;
// Class yang mengimplementasi Comparable (natural ordering)
public class Mahasiswa implements Comparable<Mahasiswa> {
private String nama;
private double ipk;
public Mahasiswa(String nama, double ipk) {
this.nama = nama;
this.ipk = ipk;
}
public String getNama() { return nama; }
public double getIpk() { return ipk; }
// Natural ordering: urutkan berdasarkan IPK descending
@Override
public int compareTo(Mahasiswa other) {
return Double.compare(other.ipk, this.ipk); // descending
}
@Override
public String toString() {
return nama + " (IPK: " + ipk + ")";
}
}
// Main class
public class SortingDemo {
public static void main(String[] args) {
List<Mahasiswa> mhs = new ArrayList<>();
mhs.add(new Mahasiswa("Budi", 3.50));
mhs.add(new Mahasiswa("Ani", 3.89));
mhs.add(new Mahasiswa("Citra", 3.75));
mhs.add(new Mahasiswa("Dina", 3.92));
// Sort menggunakan natural ordering (Comparable)
Collections.sort(mhs);
System.out.println("Urut IPK Descending:");
mhs.forEach(System.out::println);
// Dina (3.92), Ani (3.89), Citra (3.75), Budi (3.50)
// Sort dengan Comparator (custom ordering)
// Berdasarkan nama ascending
mhs.sort(Comparator.comparing(Mahasiswa::getNama));
System.out.println("\nUrut Nama:");
mhs.forEach(System.out::println);
// Berdasarkan IPK ascending
mhs.sort(Comparator.comparingDouble(Mahasiswa::getIpk));
System.out.println("\nUrut IPK Ascending:");
mhs.forEach(System.out::println);
// Comparator chaining β IPK desc, lalu nama asc
mhs.sort(Comparator.comparingDouble(Mahasiswa::getIpk).reversed()
.thenComparing(Mahasiswa::getNama));
System.out.println("\nUrut IPK Desc + Nama Asc:");
mhs.forEach(System.out::println);
// Lambda Comparator
mhs.sort((a, b) -> a.getNama().compareTo(b.getNama()));
// Comparator dengan null handling
List<String> namaList = new ArrayList<>(Arrays.asList("Budi", null, "Ani", "Citra", null));
namaList.sort(Comparator.nullsLast(Comparator.naturalOrder()));
System.out.println("\nNull-safe sort: " + namaList);
// [Ani, Budi, Citra, null, null]
}
}
7. Collections Utility Class
Class Collections (perhatikan: bukan Collection) menyediakan berbagai method utility statis untuk memanipulasi collection.
import java.util.*;
public class CollectionsUtility {
public static void main(String[] args) {
List<Integer> angka = new ArrayList<>(List.of(5, 2, 8, 1, 9, 3, 7));
// ===== SORTING =====
Collections.sort(angka); // ascending
System.out.println("Sorted: " + angka); // [1, 2, 3, 5, 7, 8, 9]
Collections.sort(angka, Collections.reverseOrder()); // descending
System.out.println("Reverse: " + angka); // [9, 8, 7, 5, 3, 2, 1]
// ===== SEARCHING =====
List<String> nama = new ArrayList<>(List.of("Budi", "Ani", "Citra"));
System.out.println("Ada Budi? " + Collections.binarySearch(nama, "Budi"));
// ===== SHUFFLE (acak) =====
Collections.shuffle(angka);
System.out.println("Shuffled: " + angka);
// ===== REVERSE =====
Collections.reverse(angka);
System.out.println("Reversed: " + angka);
// ===== MIN & MAX =====
System.out.println("Min: " + Collections.min(angka));
System.out.println("Max: " + Collections.max(angka));
// ===== FREQUENCY =====
List<String> buah = List.of("Apel", "Mangga", "Apel", "Jeruk", "Apel");
System.out.println("Frekuensi Apel: " + Collections.frequency(buah, "Apel")); // 3
// ===== UNSMODIFIABLE (immutable) =====
List<String> immutable = Collections.unmodifiableList(nama);
// immutable.add("Dina"); // UnsupportedOperationException!
// ===== EMPTY COLLECTIONS =====
List<String> empty = Collections.emptyList(); // immutable empty list
Set<Integer> emptySet = Collections.emptySet();
Map<String, Integer> emptyMap = Collections.emptyMap();
// ===== SINGLETON (satu elemen) =====
List<String> single = Collections.singletonList("Only");
System.out.println("Singleton: " + single);
// ===== SYNCHRONIZED (thread-safe) =====
List<String> syncList = Collections.synchronizedList(new ArrayList<>());
Set<String> syncSet = Collections.synchronizedSet(new HashSet<>());
Map<String, Integer> syncMap = Collections.synchronizedMap(new HashMap<>());
// ===== FILL =====
List<String> kosong = new ArrayList<>(Arrays.asList(new String[5]));
Collections.fill(kosong, "Default");
System.out.println("Fill: " + kosong); // [Default, Default, Default, Default, Default]
// ===== COPY =====
List<Integer> sumber = List.of(10, 20, 30);
List<Integer> tujuan = new ArrayList<>(Arrays.asList(new Integer[3]));
Collections.copy(tujuan, sumber);
System.out.println("Copy: " + tujuan); // [10, 20, 30]
// ===== REPLACE ALL =====
List<String> list = new ArrayList<>(List.of("A", "B", "A", "C"));
Collections.replaceAll(list, "A", "X");
System.out.println("Replace: " + list); // [X, B, X, C]
// ===== NCOPIES =====
List<String> tiga = Collections.nCopies(3, "Hello");
System.out.println("nCopies: " + tiga); // [Hello, Hello, Hello]
// ===== DISJOINT (cek apakah tidak ada irisan) =====
List<String> l1 = List.of("A", "B", "C");
List<String> l2 = List.of("D", "E", "F");
List<String> l3 = List.of("A", "D");
System.out.println("Disjoint l1-l2: " + Collections.disjoint(l1, l2)); // true
System.out.println("Disjoint l1-l3: " + Collections.disjoint(l1, l3)); // false
}
}
Collection vs Collections: Collection adalah interface dasar untuk semua collection. Collections adalah utility class dengan method-method static. Jangan tertukar! Selain itu, gunakan List.of(), Set.of(), Map.of() (Java 9+) untuk membuat collection immutable secara lebih ringkas.
8. Quiz: Uji Pemahamanmu!
Setelah membaca tutorial di atas, jawablah 5 pertanyaan berikut untuk menguji pemahamanmu tentang Java Collections Framework: