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:
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:
- Baris
nama.getFirst()dannama.getLast(): pengganti langsungget(0)danget(size() - 1)— niat terbaca jelas dan salah hitung indeks tidak mungkin terjadi. - Baris
nama.addFirst("Anto")dannama.addLast("Zaki"): penyisipan di ujung tanpa operasi geser manual; untukArrayListini setaraadd(0, x)tetapi berterimaan yang sama denganDeque. - Baris
for (var n : nama.reversed()):reversed()mengembalikan view terbalik — elemen tidak disalin, dan view tetap hidup mengikuti koleksi aslinya. - Baris
label.addFirst("nol"):LinkedHashSetmenempatkan ulang elemen ke ujung depan; pada output,nolberada sebelumalphatanpa mengganggu urutan sisanya. - Baris
populasi.firstEntry()danpopulasi.reversed().keySet():SequencedMapmenyediakan entri ujung plus tampilan map terbalik yang lengkap —keySet()-nya pun berupaSequencedSet.
| Kebutuhan | Sebelum Java 21 | Java 21+ |
|---|---|---|
Elemen terakhir List | list.get(list.size() - 1) | list.getLast() |
Elemen pertama LinkedHashSet | set.iterator().next() | set.getFirst() |
| Iterasi terbalik | Collections.reverse menyalin, atau descendingIterator khusus Deque | collection.reversed() |
Entri ujung LinkedHashMap | Loop manual seluruh entri | map.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 #
- OpenJDK: JEP 453 — Sequenced Collections
- Oracle: Interface SequencedCollection (Java API Specification)
Berkas Sumber
Berkas pendukung halaman ini dapat diunduh dan dijalankan langsung sesuai penjelasan di atas.
UjungKoleksi.java
Unduh berkasimport 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());
}
}