Setelah memahami peta besar Collections Framework, kita mulai dari cabang yang paling sering dipakai: List. Kontrak List menjanjikan tiga hal — elemen tersimpan berurutan sesuai urutan penyisipan, setiap elemen punya posisi indeks mulai dari 0, dan duplikat diperbolehkan. Ketiga janji tersebut membuat List cocok untuk data yang urutannya bermakna: daftar belanja, urutan langkah resep, riwayat transaksi, sampai daftar peserta antrean. Pada Chapter ini kita membedah kontrak List lewat operasi-operasi kuncinya, termasuk jebakan penghapusan berindeks dan API modern untuk mengakses ujung list.

Kontrak Berurutan dan Berindeks #

Sifat ordered pada List berarti urutan tidak berubah dengan sendirinya: elemen yang kita masukkan lebih dulu berada pada indeks lebih kecil, dan elemen hanya berpindah posisi jika kita memindahkannya. Sifat indexed berarti setiap elemen dapat diakses, diganti, atau dihapus berdasarkan nomor posisinya — persis seperti array, tetapi dengan ukuran yang tumbuh dan menyusut otomatis. Dua method yang sama sekali tidak dimiliki Collection adalah get(int) dan set(int, E); keduanya hanya masuk akal pada struktur yang punya posisi.

Operasi add juga punya dua bentuk. Bentuk add(e) menempelkan elemen baru di ekor list, sedangkan bentuk add(int index, e) menyisipkan elemen pada posisi tertentu dan menggeser elemen lama ke kanan. Sebaliknya, remove(int index) menghapus elemen pada posisi tertentu lalu menggeser elemen sesudahnya ke kiri untuk menutup celah.

Operasi Dasar List dalam Praktik #

Program berikut memakai daftar belanja untuk mendemonstrasikan operasi inti List: add, add berindeks, get, set, remove berindeks, indexOf, lastIndexOf, dan addAll:

import java.util.*;

public class DaftarBelanja {
    public static void main(String[] args) {
        List<String> belanja = new ArrayList<>();
        belanja.add("Beras");
        belanja.add("Minyak");
        belanja.add("Gula");
        System.out.println("Awal          : " + belanja);

        belanja.add(1, "Telur");
        System.out.println("add(1, Telur) : " + belanja);

        System.out.println("get(2)        : " + belanja.get(2));
        belanja.set(2, "Gula aren");
        System.out.println("set(2, ...)   : " + belanja);

        String dihapus = belanja.remove(0);
        System.out.println("remove(0)     : " + dihapus + " -> " + belanja);

        System.out.println("indexOf(Telur): " + belanja.indexOf("Telur"));
        belanja.add("Telur");
        System.out.println("setelah add   : " + belanja);
        System.out.println("lastIndexOf   : " + belanja.lastIndexOf("Telur"));

        List<String> tambahan = List.of("Kopi", "Teh");
        belanja.addAll(tambahan);
        System.out.println("addAll        : " + belanja);
    }
}

Output yang Dihasilkan:

text
Awal          : [Beras, Minyak, Gula]
add(1, Telur) : [Beras, Telur, Minyak, Gula]
get(2)        : Minyak
set(2, ...)   : [Beras, Telur, Gula aren, Gula]
remove(0)     : Beras -> [Telur, Gula aren, Gula]
indexOf(Telur): 0
setelah add   : [Telur, Gula aren, Gula, Telur]
lastIndexOf   : 3
addAll        : [Telur, Gula aren, Gula, Telur, Kopi, Teh]

Mari kita bedah baris-baris pentingnya:

  1. Baris 6 (List<String> belanja = new ArrayList<>();): variabel bertipe interface List menampung implementasi ArrayList. Seluruh kode berikutnya hanya bergantung pada kontrak List, bukan pada ArrayList.
  2. Baris 11 (belanja.add(1, "Telur")): Telur menyisip di indeks 1; Minyak dan Gula bergeser masing-masing ke indeks 2 dan 3 tanpa terhapus.
  3. Baris 13-15 (get(2) dan set(2, "Gula aren")): get membaca nilai pada indeks 2 tanpa mengubah list, sedangkan set menimpa nilai lama (Gula menjadi Gula aren) dan mengembalikan nilai lama tersebut.
  4. Baris 17-18 (belanja.remove(0)): menghapus elemen indeks 0 dan mengembalikan elemen yang dihapus (Beras) sehingga bisa dicetak.
  5. Baris 20-23 (indexOf dan lastIndexOf): indexOf melaporkan posisi pertama tempat elemen ditemukan; setelah Telur ditambahkan lagi di ekor, lastIndexOf melaporkan posisi terakhirnya (3). Kedua method mengembalikan -1 jika elemen tidak ditemukan.
  6. Baris 25-26 (addAll): menyerap seluruh isi tambahan ke ekor list dalam sekali panggilan, menjaga urutan elemen yang diserap.

Jebakan remove Indeks atau Nilai #

Method remove pada List mempunyai dua overload yang bentuk panggilannya nyaris sama: remove(int index) dan remove(Object o). Pada List<String> tidak masalah karena argumennya berbeda tipe, tetapi pada List<Integer> angka literal dicocokkan ke remove(int index). Program berikut memperlihatkan kedua bentuk tersebut:

import java.util.*;

public class HapusIndeksAtauNilai {
    public static void main(String[] args) {
        List<Integer> stok = new ArrayList<>(List.of(10, 20, 30));
        System.out.println("Awal      : " + stok);

        // remove(int index): menghapus elemen pada indeks 1.
        Integer hasilIndeks = stok.remove(1);
        System.out.println("remove(1) : menghapus indeks 1 (nilai " + hasilIndeks + ")");

        // remove(Object): menghapus nilai 30 jika ada.
        boolean hapusNilai = stok.remove(Integer.valueOf(30));
        System.out.println("remove(Integer.valueOf(30)): " + hapusNilai);

        // remove(Object) untuk nilai yang tidak ada -> false, tidak melempar error.
        boolean tidakAda = stok.remove(Integer.valueOf(99));
        System.out.println("remove(Integer.valueOf(99)): " + tidakAda);

        System.out.println("Akhir     : " + stok);
    }
}

Output yang Dihasilkan:

text
Awal      : [10, 20, 30]
remove(1) : menghapus indeks 1 (nilai 20)
remove(Integer.valueOf(30)): true
remove(Integer.valueOf(99)): false
Akhir     : [10]

Bedah singkatnya:

  1. Baris 7 (stok.remove(1)): literal 1 bertipe int, sehingga compiler memilih overload remove(int index) — yang terhapus adalah elemen indeks 1, yaitu nilai 20, bukan nilai 1.
  2. Baris 11 (stok.remove(Integer.valueOf(30))): argument Integer memaksa compiler memilih overload remove(Object) — yang terhapus adalah elemen bernilai 30.
  3. Baris 14 (stok.remove(Integer.valueOf(99))): remove(Object) mengembalikan false ketika nilai tidak ditemukan, tanpa melempar exception apa pun.

Akses Ujung List dengan SequencedCollection #

Sejak Java 21, List mewarisi API SequencedCollection yang menstandarkan akses ke dua ujung koleksi: getFirst(), getLast(), addFirst(e), addLast(e), dan reversed(). Method reversed() mengembalikan tampilan terbalik — list asli tidak berubah, dan perubahan pada tampilan tersebut tercermin pada list aslinya:

java UjungList.java
import java.util.*;

public class UjungList {
    public static void main(String[] args) {
        List<String> tugas = new ArrayList<>(List.of("Cuci", "Belanja", "Rapat", "Olahraga"));

        // SequencedCollection (Java 21+): akses ujung depan dan ujung belakang.
        System.out.println("getFirst(): " + tugas.getFirst());
        System.out.println("getLast() : " + tugas.getLast());
        System.out.println("reversed(): " + tugas.reversed());
        System.out.println("asli      : " + tugas);

        // addFirst/addLast juga bagian dari SequencedCollection.
        tugas.addFirst("Sarapan");
        tugas.addLast("Tidur");
        System.out.println("setelah addFirst/addLast: " + tugas);
    }
}

Output yang Dihasilkan:

text
getFirst(): Cuci
getLast() : Olahraga
reversed(): [Olahraga, Rapat, Belanja, Cuci]
asli      : [Cuci, Belanja, Rapat, Olahraga]
setelah addFirst/addLast: [Sarapan, Cuci, Belanja, Rapat, Olahraga, Tidur]

Poin pentingnya:

  1. Baris 9-10: getFirst() dan getLast() adalah padanan get(0) dan get(size() - 1) yang lebih ringkas dan bebas salah hitung indeks.
  2. Baris 11-12: reversed() mencetak urutan kebalikan, tetapi baris berikutnya membuktikan list tugas asli tidak berubah.
  3. Baris 16-17: addFirst menyisip di depan dan addLast menempel di belakang — keduanya kini berlaku untuk semua List, Deque, dan LinkedHashSet sekaligus.

Kontrak List kini sudah jelas, tetapi kontrak tersebut belum menjawab satu pertanyaan penting: implementasi mana yang sebaiknya kita pilih? Chapter berikutnya membahas pilihan paling populer — ArrayList, list berbasis array dengan akses indeks yang sangat cepat.

Referensi #

Berkas Sumber

Berkas pendukung halaman ini dapat diunduh dan dijalankan langsung sesuai penjelasan di atas.

DaftarBelanja.java

Unduh berkas
java
import java.util.*;

public class DaftarBelanja {
    public static void main(String[] args) {
        List<String> belanja = new ArrayList<>();
        belanja.add("Beras");
        belanja.add("Minyak");
        belanja.add("Gula");
        System.out.println("Awal          : " + belanja);

        belanja.add(1, "Telur");
        System.out.println("add(1, Telur) : " + belanja);

        System.out.println("get(2)        : " + belanja.get(2));
        belanja.set(2, "Gula aren");
        System.out.println("set(2, ...)   : " + belanja);

        String dihapus = belanja.remove(0);
        System.out.println("remove(0)     : " + dihapus + " -> " + belanja);

        System.out.println("indexOf(Telur): " + belanja.indexOf("Telur"));
        belanja.add("Telur");
        System.out.println("setelah add   : " + belanja);
        System.out.println("lastIndexOf   : " + belanja.lastIndexOf("Telur"));

        List<String> tambahan = List.of("Kopi", "Teh");
        belanja.addAll(tambahan);
        System.out.println("addAll        : " + belanja);
    }
}

HapusIndeksAtauNilai.java

Unduh berkas
java
import java.util.*;

public class HapusIndeksAtauNilai {
    public static void main(String[] args) {
        List<Integer> stok = new ArrayList<>(List.of(10, 20, 30));
        System.out.println("Awal      : " + stok);

        // remove(int index): menghapus elemen pada indeks 1.
        Integer hasilIndeks = stok.remove(1);
        System.out.println("remove(1) : menghapus indeks 1 (nilai " + hasilIndeks + ")");

        // remove(Object): menghapus nilai 30 jika ada.
        boolean hapusNilai = stok.remove(Integer.valueOf(30));
        System.out.println("remove(Integer.valueOf(30)): " + hapusNilai);

        // remove(Object) untuk nilai yang tidak ada -> false, tidak melempar error.
        boolean tidakAda = stok.remove(Integer.valueOf(99));
        System.out.println("remove(Integer.valueOf(99)): " + tidakAda);

        System.out.println("Akhir     : " + stok);
    }
}

UjungList.java

Unduh berkas
java
import java.util.*;

public class UjungList {
    public static void main(String[] args) {
        List<String> tugas = new ArrayList<>(List.of("Cuci", "Belanja", "Rapat", "Olahraga"));

        // SequencedCollection (Java 21+): akses ujung depan dan ujung belakang.
        System.out.println("getFirst(): " + tugas.getFirst());
        System.out.println("getLast() : " + tugas.getLast());
        System.out.println("reversed(): " + tugas.reversed());
        System.out.println("asli      : " + tugas);

        // addFirst/addLast juga bagian dari SequencedCollection.
        tugas.addFirst("Sarapan");
        tugas.addLast("Tidur");
        System.out.println("setelah addFirst/addLast: " + tugas);
    }
}