Mengambil elemen terakhir sebuah ArrayList secara klasik memakai list.get(list.size() - 1); pada LinkedHashSet kita harus bermain dengan Iterator dua langkah; pada Deque sudah tersedia getLast(). Tiga koleksi yang sama-sama punya urutan terpaksa memakai tiga gaya API berbeda — ketidakseragaman yang mengakar pada sejarah framework collections sejak Java 1.2. Sequenced collections, difinalkan di Java 21 melalui JEP 453, menyeragamkan semuanya lewat interface baru SequencedCollection, SequencedSet, dan SequencedMap: satu keluarga method untuk elemen ujung dan iterasi terbalik pada semua koleksi berurutan. Chapter ini membedah interface tersebut beserta retrofit pada koleksi lama.

Peta Interface Baru #

SequencedCollection menjadi induk baru bagi List dan Deque, sedangkan SequencedSet dan SequencedMap diimplementasikan koleksi berurutan penyisipan. Hierarkinya meneguhkan konsep: koleksi apa pun yang punya encounter order — urutan yang terdefinisi dan stabil antar operasi — kini menyatakan hal itu lewat tipe, bukan lewat dokumentasi tak tertulis. Method intinya konsisten: getFirst()/getLast() membaca ujung, addFirst()/addLast() menyisip di ujung, removeFirst()/removeLast() membuang ujung, dan reversed() mengembalikan tampilan (view) yang iterasinya terbalik tanpa menyalin elemen. Implementasi boleh menolak operasi yang tidak masuk akal baginya: List.of(...) misalnya melempar UnsupportedOperationException untuk addFirst karena immutable, namun getFirst dan reversed tetap bekerja.

Ujung Koleksi dalam Satu Gaya #

Contoh teruji berikut memakai API baru pada List, LinkedHashSet, dan LinkedHashMap sekaligus:

import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;

public class UjungKoleksi {

    public static void main(String[] args) {
        var nama = new ArrayList<>(List.of("Budi", "Sita", "Dewi", "Rina"));

        // Sebelum Java 21: nama.get(nama.size() - 1), rawan salah hitung.
        System.out.println("pertama : " + nama.getFirst());
        System.out.println("terakhir: " + nama.getLast());

        nama.addFirst("Anto");
        nama.addLast("Zaki");
        System.out.println("setelah tambah ujung : " + nama);

        System.out.print("urutan terbalik      : ");
        for (var n : nama.reversed()) {
            System.out.print(n + " ");
        }
        System.out.println();

        nama.removeFirst();
        nama.removeLast();
        System.out.println("setelah potong ujung : " + nama);

        // Set dengan urutan penyisipan: ujungnya kini satu API yang sama.
        var label = new LinkedHashSet<>(List.of("alpha", "beta", "gamma"));
        System.out.println("set pertama: " + label.getFirst() + ", terakhir: " + label.getLast());
        label.addFirst("nol");
        System.out.println("set addFirst: " + label);

        // Map dengan urutan penyisipan: entri ujung dan tampilan terbalik.
        var populasi = new LinkedHashMap<String, Integer>();
        populasi.put("Jakarta", 11);
        populasi.put("Bandung", 6);
        populasi.put("Medan", 5);
        System.out.println("map pertama : " + populasi.firstEntry());
        System.out.println("map terakhir: " + populasi.lastEntry());
        System.out.println("map dibalik : " + populasi.reversed().keySet());
    }
}

Output yang Dihasilkan:

text
pertama : Budi
terakhir: Rina
setelah tambah ujung : [Anto, Budi, Sita, Dewi, Rina, Zaki]
urutan terbalik      : Zaki Rina Dewi Sita Budi Anto 
setelah potong ujung : [Budi, Sita, Dewi, Rina]
set pertama: alpha, terakhir: gamma
set addFirst: [nol, alpha, beta, gamma]
map pertama : Jakarta=11
map terakhir: Medan=5
map dibalik : [Medan, Bandung, Jakarta]

Bedah baris-per-baris untuk bagian pentingnya:

  1. Baris nama.getFirst() dan nama.getLast(): pengganti langsung get(0) dan get(size() - 1) — niat terbaca jelas dan salah hitung indeks tidak mungkin terjadi.
  2. Baris nama.addFirst("Anto") dan nama.addLast("Zaki"): penyisipan di ujung tanpa operasi geser manual; untuk ArrayList ini setara add(0, x) tetapi berterimaan yang sama dengan Deque.
  3. Baris for (var n : nama.reversed()): reversed() mengembalikan view terbalik — elemen tidak disalin, dan view tetap hidup mengikuti koleksi aslinya.
  4. Baris label.addFirst("nol"): LinkedHashSet menempatkan ulang elemen ke ujung depan; pada output, nol berada sebelum alpha tanpa mengganggu urutan sisanya.
  5. Baris populasi.firstEntry() dan populasi.reversed().keySet(): SequencedMap menyediakan entri ujung plus tampilan map terbalik yang lengkap — keySet()-nya pun berupa SequencedSet.
KebutuhanSebelum Java 21Java 21+
Elemen terakhir Listlist.get(list.size() - 1)list.getLast()
Elemen pertama LinkedHashSetset.iterator().next()set.getFirst()
Iterasi terbalikCollections.reverse menyalin, atau descendingIterator khusus Dequecollection.reversed()
Entri ujung LinkedHashMapLoop manual seluruh entrimap.firstEntry() / map.lastEntry()

Penyeragaman ini paling terasa pada Deque. Sebelum Java 21, ArrayDeque sudah lama punya getFirst dan getLast, sementara ArrayList yang juga berurutan harus memakai indeks — dua tetangga dekat dengan kosakata berbeda. Kini keduanya mewarisi dari SequencedCollection, sehingga method generik yang menerima parameter bertipe SequencedCollection<T> bekerja untuk ArrayList, ArrayDeque, maupun LinkedHashSet tanpa cabang khusus. Untuk kode baru, aturan praktisnya sederhana: nyatakan parameter paling spesifik yang dibutuhkan — List bila pengindeksan dipakai, SequencedCollection bila cukup ujung dan urutan.

Interface SequencedCollection juga menjadi fondasi bagi API modern lain yang membutuhkan urutan andal. Berikutnya kita melompat keluar dari framework collections menuju lapisan terdekat sistem operasi: Foreign Function and Memory API yang menggantikan JNI.

Referensi #

Berkas Sumber

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

UjungKoleksi.java

Unduh berkas
java
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;

public class UjungKoleksi {

    public static void main(String[] args) {
        var nama = new ArrayList<>(List.of("Budi", "Sita", "Dewi", "Rina"));

        // Sebelum Java 21: nama.get(nama.size() - 1), rawan salah hitung.
        System.out.println("pertama : " + nama.getFirst());
        System.out.println("terakhir: " + nama.getLast());

        nama.addFirst("Anto");
        nama.addLast("Zaki");
        System.out.println("setelah tambah ujung : " + nama);

        System.out.print("urutan terbalik      : ");
        for (var n : nama.reversed()) {
            System.out.print(n + " ");
        }
        System.out.println();

        nama.removeFirst();
        nama.removeLast();
        System.out.println("setelah potong ujung : " + nama);

        // Set dengan urutan penyisipan: ujungnya kini satu API yang sama.
        var label = new LinkedHashSet<>(List.of("alpha", "beta", "gamma"));
        System.out.println("set pertama: " + label.getFirst() + ", terakhir: " + label.getLast());
        label.addFirst("nol");
        System.out.println("set addFirst: " + label);

        // Map dengan urutan penyisipan: entri ujung dan tampilan terbalik.
        var populasi = new LinkedHashMap<String, Integer>();
        populasi.put("Jakarta", 11);
        populasi.put("Bandung", 6);
        populasi.put("Medan", 5);
        System.out.println("map pertama : " + populasi.firstEntry());
        System.out.println("map terakhir: " + populasi.lastEntry());
        System.out.println("map dibalik : " + populasi.reversed().keySet());
    }
}