Tampilan
Kenapa penting
Hampir semua data di backend datang berkelompok: daftar item di keranjang, baris pesanan, hasil query produk, riwayat transaksi. Array dari M01 punya ukuran tetap — kamu harus tahu jumlah elemennya sejak awal, padahal jumlah item di keranjang terus berubah. Collection menyelesaikan masalah itu: wadah yang bisa tumbuh, menyusut, dan punya method siap pakai untuk mencari, menyisipkan, dan menghapus. List adalah collection yang paling sering kamu pakai: hampir setiap response JSON berbentuk array di aplikasi Spring Boot berasal dari sebuah List.
Konsep inti
Interface List dan implementasinya
List adalah interface di paket java.util yang mendefinisikan kumpulan elemen berurutan yang boleh berisi duplikat dan diakses lewat indeks (mulai dari 0). Implementasi yang paling umum adalah ArrayList. Sesuai prinsip program ke interface dari M02, tipe variabelnya ditulis List, dan implementasinya hanya disebut saat membuat object.
Tulisan List<String> berarti "list yang isinya String". Tipe di dalam < > adalah tipe isi collection — compiler akan menolak bila kamu memasukkan tipe lain. Di sisi kanan cukup ditulis new ArrayList<>() (disebut diamond), karena compiler sudah tahu tipe isinya dari sisi kiri. Cara kerja < > secara lengkap (generics) dibahas di M04.
java
import java.util.ArrayList;
import java.util.List;
public class ListBasics {
public static void main(String[] args) {
List<String> cart = new ArrayList<>(); // tipe isi: String
cart.add("Kopi Gayo");
cart.add("Teh Melati");
cart.add("Gula Aren");
cart.add(1, "Susu Oat"); // sisip di indeks 1
System.out.println(cart);
System.out.println(cart.size() + " item, pertama: " + cart.get(0));
cart.set(2, "Teh Hijau"); // ganti elemen di indeks 2
cart.remove("Gula Aren"); // hapus berdasarkan nilai
System.out.println(cart);
System.out.println(cart.contains("Kopi Gayo") + " " + cart.indexOf("Teh Hijau") + " " + cart.indexOf("Roti"));
for (String item : cart) {
System.out.println("- " + item);
}
cart.clear();
System.out.println(cart.isEmpty());
}
}Output:
text
[Kopi Gayo, Susu Oat, Teh Melati, Gula Aren]
4 item, pertama: Kopi Gayo
[Kopi Gayo, Susu Oat, Teh Hijau]
true 2 -1
- Kopi Gayo
- Susu Oat
- Teh Hijau
trueBerbeda dengan array, List bisa langsung dicetak — toString()-nya menampilkan isi. Ringkasan operasi yang paling sering dipakai:
| Method | Fungsi |
|---|---|
add(x), add(i, x) | tambah di akhir / sisipkan di indeks i |
get(i), set(i, x) | baca / ganti elemen di indeks i |
remove(i), remove(x) | hapus berdasarkan indeks / berdasarkan nilai |
contains(x), indexOf(x) | cek keberadaan / posisi pertama (-1 bila tidak ada) |
size(), isEmpty() | jumlah elemen / apakah kosong |
Indeks di luar rentang 0 sampai size() - 1 menghentikan program dengan IndexOutOfBoundsException.
Collection hanya berisi object: autoboxing dan jebakan remove
Collection hanya bisa menyimpan object, jadi untuk angka kamu memakai wrapper class dari M01: List<Integer>, bukan List<int>. Autoboxing membuat konversinya otomatis — tetapi ada satu jebakan terkenal: List punya dua method remove.
java
import java.util.ArrayList;
import java.util.List;
public class RemoveTrap {
public static void main(String[] args) {
List<Integer> orderIds = new ArrayList<>(List.of(10, 20, 30, 2));
orderIds.remove(2); // remove(int index) → menghapus elemen di indeks 2 (nilai 30)
System.out.println(orderIds);
orderIds.remove(Integer.valueOf(2)); // remove(Object) → menghapus nilai 2
System.out.println(orderIds);
int total = 0;
for (int id : orderIds) { // unboxing Integer → int
total += id;
}
System.out.println(total);
}
}Output: [10, 20, 2], [10, 20], 30. Untuk argumen int, compiler memilih overload remove(int index) — ingat aturan overloading di M01: tipe yang paling cocok menang. Agar yang dihapus adalah nilai, bungkus dulu menjadi Integer.
ArrayList vs LinkedList
| Aspek | ArrayList | LinkedList |
|---|---|---|
| Struktur internal | array yang otomatis diperbesar | rantai node yang saling menunjuk |
Baca get(i) | cepat — langsung ke posisi i | lambat — menelusuri node dari ujung |
| Tambah di akhir | cepat | cepat |
| Sisip/hapus di awal/tengah | menggeser elemen setelahnya | cepat bila posisinya sudah ditemukan |
| Memori | ringkas | lebih boros (tiap node menyimpan referensi) |
Di atas kertas LinkedList unggul saat menyisip di tengah, tetapi untuk menemukan posisi itu ia tetap harus menelusuri node satu per satu. Dalam praktik backend — data dibaca jauh lebih sering daripada disisipkan di tengah — ArrayList adalah pilihan default. Bila tipe variabelnya List, mengganti implementasi nanti cukup di satu baris.
List yang tidak bisa diubah
Tidak semua list boleh diubah. Daftar paket langganan, kategori tetap, atau data yang dikembalikan sebuah getter sebaiknya read-only agar tidak diubah diam-diam oleh kode lain. Java punya tiga cara dengan perilaku berbeda:
java
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class ImmutableLists {
public static void main(String[] args) {
List<String> source = new ArrayList<>(List.of("BASIC", "PRO"));
List<String> copy = List.copyOf(source); // salinan yang tidak bisa diubah
List<String> view = Collections.unmodifiableList(source); // "jendela" read-only ke source
source.add("ENTERPRISE");
System.out.println(copy);
System.out.println(view);
List<String> plans = List.of("BASIC", "PRO");
System.out.println(plans.contains("PRO"));
plans.add("ENTERPRISE");
}
}Output: [BASIC, PRO], [BASIC, PRO, ENTERPRISE], true, lalu program berhenti dengan java.lang.UnsupportedOperationException di baris plans.add(...).
List.of(...)membuat list immutable dari nilai yang kamu tulis. Semua operasi ubah (add,set,remove) melemparUnsupportedOperationException, dan elemennullditolak denganNullPointerException.List.copyOf(list)membuat salinan immutable. Perubahan pada list asal tidak ikut masuk.Collections.unmodifiableList(list)membuat view read-only: pemakainya tidak bisa mengubah, tetapi perubahan pada list asal tetap terlihat — seperti jendela kaca ke ruangan yang isinya masih bisa berubah.
ArrayList biasa dibuat dari list lain dengan new ArrayList<>(List.of(...)) — pola yang dipakai di contoh-contoh bab ini untuk mendapat list yang bisa diubah sekaligus terisi.
Sequenced collection (Java 21)
Sejak Java 21, List mendapat method dari interface SequencedCollection (JEP 431) untuk bekerja dengan ujung list tanpa menghitung indeks:
java
import java.util.ArrayList;
import java.util.List;
public class Sequenced {
public static void main(String[] args) {
List<String> history = new ArrayList<>(List.of("login", "lihat-produk", "checkout"));
System.out.println(history.getFirst());
System.out.println(history.getLast());
System.out.println(history.reversed());
history.addFirst("buka-aplikasi");
history.addLast("logout");
System.out.println(history);
}
}Output:
text
login
checkout
[checkout, lihat-produk, login]
[buka-aplikasi, login, lihat-produk, checkout, logout]getLast() jauh lebih jelas daripada history.get(history.size() - 1). reversed() mengembalikan view dengan urutan terbalik — tidak menyalin isi. Pada list kosong, getFirst() melempar NoSuchElementException, jadi cek isEmpty() lebih dulu bila list bisa kosong.
Contoh di dunia backend
Class Order menyimpan baris pesanan di List privat. Getter mengembalikan salinan defensif — persis masalah record berisi array di M02, kini dengan collection. Tanpa salinan, kode lain bisa menambah baris tanpa melewati validasi addLine.
java
import java.util.ArrayList;
import java.util.List;
record OrderLine(String sku, long unitPrice, int quantity) {
long subtotal() {
return unitPrice * quantity;
}
}
class Order {
private final String id;
private final List<OrderLine> lines = new ArrayList<>();
Order(String id) {
this.id = id;
}
void addLine(String sku, long unitPrice, int quantity) {
if (quantity <= 0) {
throw new IllegalArgumentException("Jumlah harus positif: " + quantity);
}
lines.add(new OrderLine(sku, unitPrice, quantity));
}
List<OrderLine> lines() {
return List.copyOf(lines); // salinan defensif: pemanggil tidak bisa mengubah isi order
}
long total() {
long total = 0;
for (OrderLine line : lines) {
total += line.subtotal();
}
return total;
}
@Override
public String toString() {
return id + ": " + lines.size() + " baris, Rp" + total();
}
}
public class OrderDemo {
public static void main(String[] args) {
Order order = new Order("INV-001");
order.addLine("KOPI-250", 85_000, 2);
order.addLine("TEH-100", 40_000, 1);
System.out.println(order);
List<OrderLine> snapshot = order.lines();
System.out.println(snapshot.getFirst());
System.out.println(snapshot.getLast().subtotal());
System.out.println(order);
}
}Output:
text
INV-001: 2 baris, Rp210000
OrderLine[sku=KOPI-250, unitPrice=85000, quantity=2]
40000
INV-001: 2 baris, Rp210000Field lines bertipe List dan final: referensinya tidak pernah diganti, tetapi isinya hanya berubah lewat addLine. Bila kode lain mencoba order.lines().add(...), ia mendapat UnsupportedOperationException — aturan bisnis tetap terjaga. Pola yang sama dipakai di entity dan DTO aplikasi Spring Boot.
Pertanyaan interview
1. Apa beda ArrayList dan LinkedList? Mana yang kamu pakai?
ArrayList menyimpan elemen di array internal yang diperbesar otomatis: akses berdasarkan indeks cepat, sisip di tengah perlu menggeser elemen. LinkedList adalah rantai node: sisip di ujung cepat, tetapi akses indeks harus menelusuri node dan memorinya lebih boros. Untuk hampir semua kasus backend, ArrayList pilihan default.
2. Apa beda List.of, List.copyOf, dan Collections.unmodifiableList?
List.of membuat list immutable dari nilai yang ditulis dan menolak null. List.copyOf membuat salinan immutable dari collection lain — perubahan pada sumbernya tidak ikut. Collections.unmodifiableList hanya membungkus list lain sebagai view read-only, sehingga perubahan pada list asal tetap terlihat.
3. Kenapa getter yang mengembalikan field List sebaiknya tidak mengembalikan list aslinya?
Karena pemanggil bisa mengubah isinya tanpa melewati aturan class, sehingga encapsulation bocor. Kembalikan salinan defensif (List.copyOf) atau view read-only, dan sediakan method bermakna seperti addLine untuk perubahan.
Jebakan umum
remove(int)vsremove(Object)padaList<Integer>.list.remove(2)menghapus indeks 2, bukan nilai 2. Pakailist.remove(Integer.valueOf(2))untuk menghapus nilai.- Mengubah list dari
List.of→UnsupportedOperationException. Salin kenew ArrayList<>(...)bila perlu diubah. - Mengira
unmodifiableListadalah salinan. Ia hanya view; perubahan sumbernya tetap terlihat. - Getter mengembalikan list internal sehingga pemanggil bisa menambah atau menghapus isi tanpa validasi.
getFirst()/get(0)pada list kosong →NoSuchElementException/IndexOutOfBoundsException. CekisEmpty()dulu.- Tipe variabel
ArrayListdi mana-mana. PakaiListsebagai tipe field, parameter, dan nilai kembalian.
Ringkasan
Listadalah collection berurutan yang boleh berisi duplikat dan diakses lewat indeks;ArrayListimplementasi default.List<String>berarti tipe isinyaString; collection hanya menyimpan object, jadi angka memakai wrapper.remove(int)menghapus indeks,remove(Object)menghapus nilai — hati-hati denganList<Integer>.List.ofdanList.copyOfmembuat list immutable;Collections.unmodifiableListhanya view read-only.- Java 21:
getFirst,getLast,addFirst,addLast, danreverseduntuk bekerja dengan ujung list. - Kembalikan salinan defensif dari getter agar isi object tidak bisa diubah dari luar.