sequenced: JEP 431 Sequenced Collections across List/Deque/LinkedHashSet/LinkedHashMap/TreeMap, and the getLast() replacement for the Set find-last stream tricks
This commit is contained in:
@@ -0,0 +1,28 @@
|
||||
Compiled from "SequencedCollection.java"
|
||||
public interface java.util.SequencedCollection<E> extends java.util.Collection<E> {
|
||||
public abstract java.util.SequencedCollection<E> reversed();
|
||||
public default void addFirst(E);
|
||||
public default void addLast(E);
|
||||
public default E getFirst();
|
||||
public default E getLast();
|
||||
public default E removeFirst();
|
||||
public default E removeLast();
|
||||
}
|
||||
Compiled from "SequencedMap.java"
|
||||
public interface java.util.SequencedMap<K, V> extends java.util.Map<K, V> {
|
||||
public abstract java.util.SequencedMap<K, V> reversed();
|
||||
public default java.util.Map$Entry<K, V> firstEntry();
|
||||
public default java.util.Map$Entry<K, V> lastEntry();
|
||||
public default java.util.Map$Entry<K, V> pollFirstEntry();
|
||||
public default java.util.Map$Entry<K, V> pollLastEntry();
|
||||
public default V putFirst(K, V);
|
||||
public default V putLast(K, V);
|
||||
public default java.util.SequencedSet<K> sequencedKeySet();
|
||||
public default java.util.SequencedCollection<V> sequencedValues();
|
||||
public default java.util.SequencedSet<java.util.Map$Entry<K, V>> sequencedEntrySet();
|
||||
}
|
||||
Compiled from "SequencedSet.java"
|
||||
public interface java.util.SequencedSet<E> extends java.util.SequencedCollection<E>, java.util.Set<E> {
|
||||
public abstract java.util.SequencedSet<E> reversed();
|
||||
public default java.util.SequencedCollection reversed();
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
=== List (ArrayList implements SequencedCollection) ===
|
||||
getFirst() = alpha
|
||||
getLast() = gamma
|
||||
after addLast("delta"): [alpha, beta, gamma, delta]
|
||||
reversed() = [delta, gamma, beta, alpha]
|
||||
|
||||
=== Deque (ArrayDeque implements SequencedCollection) ===
|
||||
getFirst() = 1
|
||||
getLast() = 3
|
||||
reversed() = [3, 2, 1]
|
||||
|
||||
=== LinkedHashSet implements SequencedSet (insertion order preserved) ===
|
||||
getFirst() = red
|
||||
getLast() = blue
|
||||
reversed() = [blue, green, red]
|
||||
|
||||
=== LinkedHashMap implements SequencedMap (insertion order) ===
|
||||
firstEntry() = jan=1
|
||||
lastEntry() = mar=3
|
||||
after putFirst("dec", 12): {dec=12, jan=1, feb=2, mar=3}
|
||||
sequencedKeySet() = [dec, jan, feb, mar]
|
||||
reversed() = {mar=3, feb=2, jan=1, dec=12}
|
||||
|
||||
=== TreeMap implements SequencedMap too (order = comparator order, not insertion) ===
|
||||
tree contents (natural order) = {1=one, 2=two, 3=three}
|
||||
firstEntry() = 1=one
|
||||
lastEntry() = 3=three
|
||||
reversed() = {3=three, 2=two, 1=one}
|
||||
@@ -0,0 +1,22 @@
|
||||
A LinkedHashSet has a real iteration order, but pre-Java-21 Set has no getLast():
|
||||
[home, search, product/42, cart, checkout]
|
||||
|
||||
Old #1 - stream().reduce((a,b) -> b): checkout
|
||||
Old #2 - stream().skip(size - 1).findFirst(): checkout
|
||||
Old #3 - new ArrayList<>(set).get(size-1): checkout
|
||||
New - set.getLast(): checkout
|
||||
|
||||
All four agree: true
|
||||
|
||||
Why the old ways were worse than they looked:
|
||||
- reduce((a,b)->b) walks the ENTIRE set, every time: O(n).
|
||||
- skip(size-1).findFirst() also walks the entire stream pipeline up to size-1: O(n).
|
||||
- new ArrayList<>(set) copies every element just to throw the copy away: O(n) time AND space.
|
||||
- set.getLast() walks the SequencedCollection's default path to reversed().iterator().next();
|
||||
for LinkedHashSet specifically, that's backed by LinkedHashMap's existing doubly-linked
|
||||
insertion-order list, so it resolves in O(1) in the JDK's actual implementation - though
|
||||
that O(1) is an implementation detail of LinkedHashMap, not a guarantee the interface makes.
|
||||
|
||||
On a List this was never actually hard - list.get(list.size()-1) already worked.
|
||||
list.getLast() = c (same answer, no size() arithmetic, and it now
|
||||
reads identically whether the underlying type is a List, a Deque, or a LinkedHashSet).
|
||||
@@ -0,0 +1,15 @@
|
||||
original = [10, 20, 30]
|
||||
reversedView = [30, 20, 10]
|
||||
|
||||
Mutating the ORIGINAL list (original.add(40)):
|
||||
original = [10, 20, 30, 40]
|
||||
reversedView = [40, 30, 20, 10] <- updated with no code touching reversedView directly
|
||||
|
||||
Mutating the REVERSED VIEW (reversedView.addFirst(0)):
|
||||
reversedView = [0, 40, 30, 20, 10]
|
||||
original = [10, 20, 30, 40, 0] <- the 0 landed at the END of the original list
|
||||
|
||||
This is a VIEW, confirmed structurally: reversed().reversed() is back to the
|
||||
same logical order as the original, still backed by the same list:
|
||||
letters = [x, y, zed]
|
||||
letters.reversed().reversed() = [x, y, zed] (back to original order, same backing list)
|
||||
@@ -0,0 +1,4 @@
|
||||
-------------------------------------------------------------------------------
|
||||
Test set: com.ankurm.sequenced.SequencedCollectionsTest
|
||||
-------------------------------------------------------------------------------
|
||||
Tests run: 6, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.158 s -- in com.ankurm.sequenced.SequencedCollectionsTest
|
||||
Reference in New Issue
Block a user