🗃️ Collections Framework · Intermediate

Sequenced collections in Java

Java 21 getFirst, getLast, reversed() on List, Deque, LinkedHashSet, LinkedHashMap.

🧩 The mysteryQuick: how do you get the last element of a LinkedHashSet in Java 17? You can't, not without walking the whole set. Java 21 changed that for an entire family of collections.

Goodbye size() - 1

Java 21 added **SequencedCollection: collections with a defined encounter order got one uniform API. getFirst, getLast, addFirst, addLast, removeFirst, removeLast and reversed()**.

var l = new ArrayList<>(List.of("a", "b"));
l.getFirst(); // "a" (was l.get(0))
l.getLast();  // "b"
// was l.get(l.size() - 1)

Who joined the club

List, Deque, LinkedHashSet, SortedSet (TreeSet), LinkedHashMap and SortedMap (TreeMap) are all sequenced. HashSet and HashMap are not: they have no defined encounter order, so "first" would mean nothing.

🔮 Predict it

Your turn

What does this print?

var list = new ArrayList<>(List.of("a", "b"));
var rev = list.reversed();
list.add("c");
System.out.println(rev);
  1. [b, a]
  2. [c, b, a]
  3. [a, b, c]
Show the answer

**reversed() returns a live view**, not a copy. The later add("c") shows up in it, at the front of the reversed order: [c, b, a].

Sequenced maps

**SequencedMap** brings firstEntry(), lastEntry(), pollFirstEntry(), plus **putFirst and putLast**, which place a mapping at the front or back of a LinkedHashMap's order.

var m = new LinkedHashMap<String, Integer>();
m.put("a", 1);
m.putFirst("z", 0);   // {z=0, a=1}
m.lastEntry();        // a=1
🔮 Predict it

Jump the queue

What does this print?

var m = new LinkedHashMap<String, Integer>();
m.put("a", 1);
m.put("b", 2);
m.putFirst("c", 3);
var f = m.firstEntry();
System.out.println(f + " " + m.lastEntry());
  1. a=1 b=2
  2. c=3 b=2
  3. c=3 a=1
Show the answer

putFirst inserts c=3 at the front of the encounter order. The last entry is still b=2.

⚠️ The trap

Empty means exception

On an empty collection, **getFirst() throws NoSuchElementException**, following the Deque convention. The old get(0) throws IndexOutOfBoundsException instead. Neither returns null.

List<String> empty = new ArrayList<>();
empty.getFirst(); // NoSuchElementException
empty.get(0);     // IndexOutOfBoundsException
💼 In the real world

Cleaner code

"Latest message", "oldest log entry", "most recently used item": these are now getLast(), getFirst() and reversed() one-liners. Fewer off-by-one errors, and code that works the same whether the collection is a List, a Deque or a LinkedHashSet.

Key takeaways

  1. list.getFirst() replaces list.get(0); getLast() replaces get(size() - 1)
  2. reversed() returns a reverse-ordered VIEW, not a copy
  3. SequencedMap adds firstEntry, lastEntry, putFirst, putLast
  4. On an empty collection, getFirst() throws NoSuchElementException
🤯 Did you know?

Sequenced collections arrived via JEP 431 in Java 21. Before them, the only way to get the last element of a LinkedHashSet was to iterate through every element.

Practice questions

What does this print?

var list = new ArrayList<>(List.of(1, 2, 3));
var rev = list.reversed();
list.add(4);
System.out.println(rev);
  1. [3, 2, 1]
  2. [4, 3, 2, 1]
  3. [1, 2, 3, 4]
  4. [3, 2, 1, 4]
Check your answer

[4, 3, 2, 1]. reversed() returns a view over the original list, so the later add(4) shows up — at the front of the reversed view.

What does this print?

var m = new LinkedHashMap<String, Integer>();
m.put("a", 1);
m.put("b", 2);
m.putFirst("z", 0);
System.out.println(m.firstEntry());
System.out.println(m.lastEntry());
  1. a=1 b=2
  2. z=0 b=2
  3. z=0 a=1
  4. a=1 z=0
Check your answer

z=0 b=2. putFirst (Java 21) inserts the mapping at the front of the LinkedHashMap's encounter order. So the first entry is z=0 and the last is still b=2.

Next: the final boss of this world. Which collection should you pick? Big-O tells you.