diff --git a/src/java.base/share/classes/java/util/AbstractMap.java b/src/java.base/share/classes/java/util/AbstractMap.java
index 8e9414f8ef54f..b0d311f656565 100644
--- a/src/java.base/share/classes/java/util/AbstractMap.java
+++ b/src/java.base/share/classes/java/util/AbstractMap.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1997, 2022, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2023, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -25,6 +25,11 @@
package java.util;
+import java.util.stream.Stream;
+import java.util.function.Consumer;
+import java.util.function.IntFunction;
+import java.util.function.Predicate;
+
/**
* This class provides a skeletal implementation of the {@code Map}
* interface, to minimize the effort required to implement this interface.
@@ -875,7 +880,48 @@ public int hashCode() {
public String toString() {
return key + "=" + value;
}
-
}
+ /**
+ * Delegates all Collection methods to the provided non-sequenced map view,
+ * except add() and addAll(), which throw UOE. This provides the common
+ * implementation of each of the sequenced views of the SequencedMap.
+ * Each view implementation is a subclass that provides an instance of the
+ * non-sequenced view as a delegate and an implementation of reversed().
+ * Each view also inherits the default implementations for the sequenced
+ * methods from SequencedCollection or SequencedSet.
+ *
+ * Ideally this would be a private class within SequencedMap, but private
+ * classes aren't permitted within interfaces.
+ *
+ * @param the view's element type
+ */
+ /* non-public */ abstract static class ViewCollection implements Collection {
+ UnsupportedOperationException uoe() { return new UnsupportedOperationException(); }
+ final Collection view;
+
+ ViewCollection(Collection view) { this.view = view; }
+
+ public boolean add(E t) { throw uoe(); }
+ public boolean addAll(Collection extends E> c) { throw uoe(); }
+ public void clear() { view.clear(); }
+ public boolean contains(Object o) { return view.contains(o); }
+ public boolean containsAll(Collection> c) { return view.containsAll(c); }
+ public boolean equals(Object o) { return view.equals(o); }
+ public void forEach(Consumer super E> c) { view.forEach(c); }
+ public int hashCode() { return view.hashCode(); }
+ public boolean isEmpty() { return view.isEmpty(); }
+ public Iterator iterator() { return view.iterator(); }
+ public Stream parallelStream() { return view.parallelStream(); }
+ public boolean remove(Object o) { return view.remove(o); }
+ public boolean removeAll(Collection> c) { return view.removeAll(c); }
+ public boolean removeIf(Predicate super E> filter) { return view.removeIf(filter); }
+ public boolean retainAll(Collection> c) { return view.retainAll(c); }
+ public int size() { return view.size(); }
+ public Spliterator spliterator() { return view.spliterator(); }
+ public Stream stream() { return view.stream(); }
+ public Object[] toArray() { return view.toArray(); }
+ public T[] toArray(IntFunction generator) { return view.toArray(generator); }
+ public T[] toArray(T[] a) { return view.toArray(a); }
+ }
}
diff --git a/src/java.base/share/classes/java/util/ArrayDeque.java b/src/java.base/share/classes/java/util/ArrayDeque.java
index 603ebceb757df..eb459b805174b 100644
--- a/src/java.base/share/classes/java/util/ArrayDeque.java
+++ b/src/java.base/share/classes/java/util/ArrayDeque.java
@@ -74,9 +74,8 @@
* exception for its correctness: the fail-fast behavior of iterators
* should be used only to detect bugs.
*
- * This class and its iterator implement all of the
- * optional methods of the {@link Collection} and {@link
- * Iterator} interfaces.
+ *
This class and its iterator implement all of the optional methods of the
+ * {@link Collection}, {@link SequencedCollection}, and {@link Iterator} interfaces.
*
*
This class is a member of the
*
diff --git a/src/java.base/share/classes/java/util/ArrayList.java b/src/java.base/share/classes/java/util/ArrayList.java
index a36dcd8a79695..1f27572ef167b 100644
--- a/src/java.base/share/classes/java/util/ArrayList.java
+++ b/src/java.base/share/classes/java/util/ArrayList.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1997, 2019, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2023, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -428,6 +428,35 @@ public E get(int index) {
return elementData(index);
}
+ /**
+ * {@inheritDoc}
+ *
+ * @throws NoSuchElementException {@inheritDoc}
+ * @since 21
+ */
+ public E getFirst() {
+ if (size == 0) {
+ throw new NoSuchElementException();
+ } else {
+ return elementData(0);
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @throws NoSuchElementException {@inheritDoc}
+ * @since 21
+ */
+ public E getLast() {
+ int last = size - 1;
+ if (last < 0) {
+ throw new NoSuchElementException();
+ } else {
+ return elementData(last);
+ }
+ }
+
/**
* Replaces the element at the specified position in this list with
* the specified element.
@@ -491,6 +520,24 @@ public void add(int index, E element) {
size = s + 1;
}
+ /**
+ * {@inheritDoc}
+ *
+ * @since 21
+ */
+ public void addFirst(E element) {
+ add(0, element);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @since 21
+ */
+ public void addLast(E element) {
+ add(element);
+ }
+
/**
* Removes the element at the specified position in this list.
* Shifts any subsequent elements to the left (subtracts one from their
@@ -510,6 +557,41 @@ public E remove(int index) {
return oldValue;
}
+ /**
+ * {@inheritDoc}
+ *
+ * @throws NoSuchElementException {@inheritDoc}
+ * @since 21
+ */
+ public E removeFirst() {
+ if (size == 0) {
+ throw new NoSuchElementException();
+ } else {
+ Object[] es = elementData;
+ @SuppressWarnings("unchecked") E oldValue = (E) es[0];
+ fastRemove(es, 0);
+ return oldValue;
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @throws NoSuchElementException {@inheritDoc}
+ * @since 21
+ */
+ public E removeLast() {
+ int last = size - 1;
+ if (last < 0) {
+ throw new NoSuchElementException();
+ } else {
+ Object[] es = elementData;
+ @SuppressWarnings("unchecked") E oldValue = (E) es[last];
+ fastRemove(es, last);
+ return oldValue;
+ }
+ }
+
/**
* {@inheritDoc}
*/
diff --git a/src/java.base/share/classes/java/util/Collection.java b/src/java.base/share/classes/java/util/Collection.java
index fbaa1881a899e..0f63f0b26a46b 100644
--- a/src/java.base/share/classes/java/util/Collection.java
+++ b/src/java.base/share/classes/java/util/Collection.java
@@ -33,8 +33,11 @@
/**
* The root interface in the collection hierarchy. A collection
* represents a group of objects, known as its elements. Some
- * collections allow duplicate elements and others do not. Some are ordered
- * and others unordered. The JDK does not provide any direct
+ * collections allow duplicate elements and others do not. Some are ordered,
+ * and others are unordered. Collections that have a defined
+ * encounter order
+ * are generally subtypes of the {@link SequencedCollection} interface.
+ * The JDK does not provide any direct
* implementations of this interface: it provides implementations of more
* specific subinterfaces like {@code Set} and {@code List}. This interface
* is typically used to pass collections around and manipulate them where
@@ -121,8 +124,9 @@
* Other examples of view collections include collections that provide a
* different representation of the same elements, for example, as
* provided by {@link List#subList List.subList},
- * {@link NavigableSet#subSet NavigableSet.subSet}, or
- * {@link Map#entrySet Map.entrySet}.
+ * {@link NavigableSet#subSet NavigableSet.subSet},
+ * {@link Map#entrySet Map.entrySet}, or
+ * {@link SequencedCollection#reversed SequencedCollection.reversed}.
* Any changes made to the backing collection are visible in the view collection.
* Correspondingly, any changes made to the view collection — if changes
* are permitted — are written through to the backing collection.
@@ -202,7 +206,8 @@
* serializability of such collections is described in the specification of the method
* that creates them, or in some other suitable place. In cases where the serializability
* of a collection is not specified, there is no guarantee about the serializability of such
- * collections. In particular, many view collections are not serializable.
+ * collections. In particular, many view collections are not serializable,
+ * even if the original collection is serializable.
*
*
A collection implementation that implements the {@code Serializable} interface cannot
* be guaranteed to be serializable. The reason is that in general, collections
@@ -501,7 +506,9 @@ default T[] toArray(IntFunction generator) {
* the specified collection is modified while the operation is in progress.
* (This implies that the behavior of this call is undefined if the
* specified collection is this collection, and this collection is
- * nonempty.)
+ * nonempty.) If the specified collection has a defined
+ * encounter order,
+ * processing of its elements generally occurs in that order.
*
* @param c collection containing elements to be added to this collection
* @return {@code true} if this collection changed as a result of the call
diff --git a/src/java.base/share/classes/java/util/Collections.java b/src/java.base/share/classes/java/util/Collections.java
index 4425b4483c0bc..f45d297f3007b 100644
--- a/src/java.base/share/classes/java/util/Collections.java
+++ b/src/java.base/share/classes/java/util/Collections.java
@@ -369,9 +369,15 @@ else if (cmp > 0)
*
* This method runs in linear time.
*
+ * @apiNote
+ * This method mutates the specified list in-place. To obtain a
+ * reverse-ordered view of a list without mutating it, use the
+ * {@link List#reversed List.reversed} method.
+ *
* @param list the list whose elements are to be reversed.
* @throws UnsupportedOperationException if the specified list or
* its list-iterator does not support the {@code set} operation.
+ * @see List#reversed List.reversed
*/
@SuppressWarnings({"rawtypes", "unchecked"})
public static void reverse(List> list) {
@@ -1130,6 +1136,87 @@ public Stream parallelStream() {
}
}
+ /**
+ * Returns an unmodifiable view of the
+ * specified {@code SequencedCollection}. Query operations on the returned collection
+ * "read through" to the specified collection, and attempts to modify the returned
+ * collection, whether direct or via its iterator, result in an
+ * {@code UnsupportedOperationException}.
+ *
+ * The returned collection does not pass the {@code hashCode} and
+ * {@code equals} operations through to the backing collection, but relies on
+ * {@code Object}'s {@code equals} and {@code hashCode} methods. This
+ * is necessary to preserve the contracts of these operations in the case
+ * that the backing collection is a set or a list.
+ *
+ * The returned collection will be serializable if the specified collection
+ * is serializable.
+ *
+ * @implNote This method may return its argument if the argument is already unmodifiable.
+ * @param the class of the objects in the collection
+ * @param c the collection for which an unmodifiable view is to be
+ * returned.
+ * @return an unmodifiable view of the specified collection.
+ * @since 21
+ */
+ @SuppressWarnings("unchecked")
+ public static SequencedCollection unmodifiableSequencedCollection(SequencedCollection extends T> c) {
+ if (c.getClass() == UnmodifiableSequencedCollection.class) {
+ return (SequencedCollection) c;
+ }
+ return new UnmodifiableSequencedCollection<>(c);
+ }
+
+ /**
+ * @serial include
+ */
+ static class UnmodifiableSequencedCollection extends UnmodifiableCollection
+ implements SequencedCollection, Serializable {
+
+ @java.io.Serial
+ private static final long serialVersionUID = -6060065079711684830L;
+
+ UnmodifiableSequencedCollection(SequencedCollection extends E> c) {
+ super(c);
+ }
+
+ @SuppressWarnings("unchecked")
+ private SequencedCollection sc() {
+ return (SequencedCollection) c;
+ }
+
+ // Even though this wrapper class is serializable, the reversed view is effectively
+ // not serializable because it points to the reversed collection view, which usually isn't
+ // serializable.
+ public SequencedCollection reversed() {
+ return new UnmodifiableSequencedCollection<>(sc().reversed());
+ }
+
+ public void addFirst(E e) {
+ throw new UnsupportedOperationException();
+ }
+
+ public void addLast(E e) {
+ throw new UnsupportedOperationException();
+ }
+
+ public E getFirst() {
+ return sc().getFirst();
+ }
+
+ public E getLast() {
+ return sc().getLast();
+ }
+
+ public E removeFirst() {
+ throw new UnsupportedOperationException();
+ }
+
+ public E removeLast() {
+ throw new UnsupportedOperationException();
+ }
+ }
+
/**
* Returns an unmodifiable view of the
* specified set. Query operations on the returned set "read through" to the specified
@@ -1166,6 +1253,56 @@ static class UnmodifiableSet extends UnmodifiableCollection
public int hashCode() {return c.hashCode();}
}
+ /**
+ * Returns an unmodifiable view of the
+ * specified {@code SequencedSet}. Query operations on the returned set
+ * "read through" to the specified set, and attempts to modify the returned
+ * set, whether direct or via its iterator, result in an
+ * {@code UnsupportedOperationException}.
+ *
+ * The returned set will be serializable if the specified set
+ * is serializable.
+ *
+ * @implNote This method may return its argument if the argument is already unmodifiable.
+ * @param the class of the objects in the set
+ * @param s the set for which an unmodifiable view is to be returned.
+ * @return an unmodifiable view of the specified sequenced set.
+ * @since 21
+ */
+ @SuppressWarnings("unchecked")
+ public static SequencedSet unmodifiableSequencedSet(SequencedSet extends T> s) {
+ // Not checking for subclasses because of heap pollution and information leakage.
+ if (s.getClass() == UnmodifiableSequencedSet.class) {
+ return (SequencedSet) s;
+ }
+ return new UnmodifiableSequencedSet<>(s);
+ }
+
+ /**
+ * @serial include
+ */
+ static class UnmodifiableSequencedSet extends UnmodifiableSequencedCollection
+ implements SequencedSet, Serializable {
+ @java.io.Serial
+ private static final long serialVersionUID = -2153469532349793522L;
+
+ UnmodifiableSequencedSet(SequencedSet extends E> s) {super(s);}
+ public boolean equals(Object o) {return o == this || c.equals(o);}
+ public int hashCode() {return c.hashCode();}
+
+ @SuppressWarnings("unchecked")
+ private SequencedSet ss() {
+ return (SequencedSet) c;
+ }
+
+ // Even though this wrapper class is serializable, the reversed view is effectively
+ // not serializable because it points to the reversed set view, which usually isn't
+ // serializable.
+ public SequencedSet reversed() {
+ return new UnmodifiableSequencedSet<>(ss().reversed());
+ }
+ }
+
/**
* Returns an unmodifiable view of the
* specified sorted set. Query operations on the returned sorted set "read
@@ -1504,7 +1641,7 @@ private static class UnmodifiableMap implements Map, Serializable {
private static final long serialVersionUID = -1034234728574286014L;
@SuppressWarnings("serial") // Conditionally serializable
- private final Map extends K, ? extends V> m;
+ final Map extends K, ? extends V> m;
UnmodifiableMap(Map extends K, ? extends V> m) {
if (m==null)
@@ -1828,6 +1965,72 @@ && eq(e.getKey(), t.getKey())
}
}
+ /**
+ * Returns an unmodifiable view of the
+ * specified {@code SequencedMap}. Query operations on the returned map
+ * "read through" to the specified map, and attempts to modify the returned
+ * map, whether direct or via its collection views, result in an
+ * {@code UnsupportedOperationException}.
+ *
+ * The returned map will be serializable if the specified map
+ * is serializable.
+ *
+ * @implNote This method may return its argument if the argument is already unmodifiable.
+ * @param the class of the map keys
+ * @param the class of the map values
+ * @param m the map for which an unmodifiable view is to be returned.
+ * @return an unmodifiable view of the specified map.
+ * @since 21
+ */
+ @SuppressWarnings("unchecked")
+ public static SequencedMap unmodifiableSequencedMap(SequencedMap extends K, ? extends V> m) {
+ // Not checking for subclasses because of heap pollution and information leakage.
+ if (m.getClass() == UnmodifiableSequencedMap.class) {
+ return (SequencedMap) m;
+ }
+ return new UnmodifiableSequencedMap<>(m);
+ }
+
+ /**
+ * @serial include
+ */
+ private static class UnmodifiableSequencedMap extends UnmodifiableMap implements SequencedMap, Serializable {
+ @java.io.Serial
+ private static final long serialVersionUID = -8171676257373950636L;
+
+ UnmodifiableSequencedMap(Map extends K, ? extends V> m) {
+ super(m);
+ }
+
+ @SuppressWarnings("unchecked")
+ private SequencedMap sm() {
+ return (SequencedMap) m;
+ }
+
+ // Even though this wrapper class is serializable, the reversed view is effectively
+ // not serializable because it points to the reversed map view, which usually isn't
+ // serializable.
+ public SequencedMap reversed() {
+ return new UnmodifiableSequencedMap<>(sm().reversed());
+ }
+
+ public Entry pollFirstEntry() {
+ throw new UnsupportedOperationException();
+ }
+
+ public Entry pollLastEntry() {
+ throw new UnsupportedOperationException();
+ }
+
+ public V putFirst(K k, V v) {
+ throw new UnsupportedOperationException();
+ }
+
+ public V putLast(K k, V v) {
+ throw new UnsupportedOperationException();
+ }
+ }
+
/**
* Returns an unmodifiable view of the
* specified sorted map. Query operations on the returned sorted map "read through"
@@ -5326,6 +5529,14 @@ private void readObject(ObjectInputStream ois) throws IOException, ClassNotFound
*
* The returned comparator is serializable.
*
+ * @apiNote
+ * This method returns a {@code Comparator} that is suitable for sorting
+ * elements in reverse order. To obtain a reverse-ordered view of a
+ * sequenced collection, use the {@link SequencedCollection#reversed
+ * SequencedCollection.reversed} method. Or, to obtain a reverse-ordered
+ * view of a sequenced map, use the {@link SequencedMap#reversed
+ * SequencedMap.reversed} method.
+ *
* @param the class of the objects compared by the comparator
* @return A comparator that imposes the reverse of the natural
* ordering on a collection of objects that implement
@@ -5372,6 +5583,14 @@ public Comparator> reversed() {
* The returned comparator is serializable (assuming the specified
* comparator is also serializable or {@code null}).
*
+ * @apiNote
+ * This method returns a {@code Comparator} that is suitable for sorting
+ * elements in reverse order. To obtain a reverse-ordered view of a
+ * sequenced collection, use the {@link SequencedCollection#reversed
+ * SequencedCollection.reversed} method. Or, to obtain a reverse-ordered
+ * view of a sequenced map, use the {@link SequencedMap#reversed
+ * SequencedMap.reversed} method.
+ *
* @param the class of the objects compared by the comparator
* @param cmp a comparator who's ordering is to be reversed by the returned
* comparator or {@code null}
@@ -5682,6 +5901,8 @@ public static boolean addAll(Collection super T> c, T... elements) {
* @since 1.6
*/
public static Set newSetFromMap(Map map) {
+ if (! map.isEmpty()) // implicit null check
+ throw new IllegalArgumentException("Map is non-empty");
return new SetFromMap<>(map);
}
@@ -5692,12 +5913,10 @@ private static class SetFromMap extends AbstractSet
implements Set, Serializable
{
@SuppressWarnings("serial") // Conditionally serializable
- private final Map m; // The backing map
+ final Map m; // The backing map
private transient Set s; // Its keySet
SetFromMap(Map map) {
- if (!map.isEmpty())
- throw new IllegalArgumentException("Map is non-empty");
m = map;
s = map.keySet();
}
@@ -5746,6 +5965,91 @@ private void readObject(java.io.ObjectInputStream stream)
stream.defaultReadObject();
s = m.keySet();
}
+
+ @java.io.Serial
+ private void readObjectNoData() throws java.io.ObjectStreamException {
+ throw new java.io.InvalidObjectException("missing SetFromMap data");
+ }
+ }
+
+ /**
+ * Returns a sequenced set backed by the specified map. The resulting set displays
+ * the same ordering, concurrency, and performance characteristics as the
+ * backing map. In essence, this factory method provides a {@link SequencedSet}
+ * implementation corresponding to any {@link SequencedMap} implementation.
+ *
+ * Each method invocation on the set returned by this method results in
+ * exactly one method invocation on the backing map or its {@code keySet}
+ * view, with one exception. The {@code addAll} method is implemented
+ * as a sequence of {@code put} invocations on the backing map.
+ *
+ *
The specified map must be empty at the time this method is invoked,
+ * and should not be accessed directly after this method returns. These
+ * conditions are ensured if the map is created empty, passed directly
+ * to this method, and no reference to the map is retained.
+ *
+ * @apiNote
+ * The following example code creates a {@code SequencedSet} from a
+ * {@code LinkedHashMap}. This differs from a {@code LinkedHashSet}
+ * in that the map's {@code removeEldestEntry} is overridden to provide
+ * an eviction policy, which is not possible with a {@code LinkedHashSet}.
+ *
+ * {@snippet :
+ * SequencedSet set = Collections.newSequencedSetFromMap(
+ * new LinkedHashMap() {
+ * protected boolean removeEldestEntry(Map.Entry e) {
+ * return this.size() > 5;
+ * }
+ * });
+ * }
+ *
+ * @param the class of the map keys and of the objects in the
+ * returned set
+ * @param map the backing map
+ * @return the set backed by the map
+ * @throws IllegalArgumentException if {@code map} is not empty
+ * @since 21
+ */
+ public static SequencedSet newSequencedSetFromMap(SequencedMap map) {
+ if (! map.isEmpty()) // implicit null check
+ throw new IllegalArgumentException("Map is non-empty");
+ return new SequencedSetFromMap<>(map);
+ }
+
+ /**
+ * @serial include
+ */
+ private static class SequencedSetFromMap extends SetFromMap implements SequencedSet {
+ private E nsee(Map.Entry e) {
+ if (e == null) {
+ throw new NoSuchElementException();
+ } else {
+ return e.getKey();
+ }
+ }
+
+ private SequencedMap map() {
+ return (SequencedMap) super.m;
+ }
+
+ SequencedSetFromMap(SequencedMap map) {
+ super(map);
+ }
+
+ // Even though this wrapper class is serializable, the reversed view is effectively
+ // not serializable because it points to the reversed map view, which usually isn't
+ // serializable.
+ public SequencedSet reversed() { return new SequencedSetFromMap<>(map().reversed()); }
+
+ public void addFirst(E e) { map().putFirst(e, Boolean.TRUE); }
+ public void addLast(E e) { map().putLast(e, Boolean.TRUE); }
+ public E getFirst() { return nsee(map().firstEntry()); }
+ public E getLast() { return nsee(map().lastEntry()); }
+ public E removeFirst() { return nsee(map().pollFirstEntry()); }
+ public E removeLast() { return nsee(map().pollLastEntry()); }
+
+ @java.io.Serial
+ private static final long serialVersionUID = -3943479744841433802L;
}
/**
@@ -5761,6 +6065,11 @@ private void readObject(java.io.ObjectInputStream stream)
* implemented as a sequence of {@link Deque#addFirst addFirst}
* invocations on the backing deque.
*
+ * @apiNote
+ * This method provides a view that inverts the sense of certain operations,
+ * but it doesn't reverse the encounter order. To obtain a reverse-ordered
+ * view, use the {@link Deque#reversed Deque.reversed} method.
+ *
* @param the class of the objects in the deque
* @param deque the deque
* @return the queue
diff --git a/src/java.base/share/classes/java/util/Deque.java b/src/java.base/share/classes/java/util/Deque.java
index 90d7292c31a90..13a2e150d97c6 100644
--- a/src/java.base/share/classes/java/util/Deque.java
+++ b/src/java.base/share/classes/java/util/Deque.java
@@ -201,7 +201,7 @@
* @since 1.6
* @param the type of elements held in this deque
*/
-public interface Deque extends Queue {
+public interface Deque extends Queue, SequencedCollection {
/**
* Inserts the specified element at the front of this deque if it is
* possible to do so immediately without violating capacity restrictions,
@@ -613,4 +613,17 @@ public interface Deque extends Queue {
*/
Iterator descendingIterator();
+ /**
+ * {@inheritDoc}
+ *
+ * @implSpec
+ * The implementation in this interface returns an instance of a reverse-ordered
+ * Deque that delegates its operations to this Deque.
+ *
+ * @return a reverse-ordered view of this collection, as a {@code Deque}
+ * @since 21
+ */
+ default Deque reversed() {
+ return ReverseOrderDequeView.of(this);
+ }
}
diff --git a/src/java.base/share/classes/java/util/HashSet.java b/src/java.base/share/classes/java/util/HashSet.java
index 9d4cdb346f2ac..e59bf7b089a6a 100644
--- a/src/java.base/share/classes/java/util/HashSet.java
+++ b/src/java.base/share/classes/java/util/HashSet.java
@@ -94,10 +94,10 @@ public class HashSet
@java.io.Serial
static final long serialVersionUID = -5024744406713321676L;
- private transient HashMap map;
+ transient HashMap map;
// Dummy value to associate with an Object in the backing Map
- private static final Object PRESENT = new Object();
+ static final Object PRESENT = new Object();
/**
* Constructs a new, empty set; the backing {@code HashMap} instance has
diff --git a/src/java.base/share/classes/java/util/ImmutableCollections.java b/src/java.base/share/classes/java/util/ImmutableCollections.java
index 3de7e1d5eae79..398ebb14a54da 100644
--- a/src/java.base/share/classes/java/util/ImmutableCollections.java
+++ b/src/java.base/share/classes/java/util/ImmutableCollections.java
@@ -331,6 +331,11 @@ public boolean contains(Object o) {
return indexOf(o) >= 0;
}
+ @Override
+ public List reversed() {
+ return ReverseOrderListView.of(this, false);
+ }
+
IndexOutOfBoundsException outOfBounds(int index) {
return new IndexOutOfBoundsException("Index: " + index + " Size: " + size());
}
diff --git a/src/java.base/share/classes/java/util/LinkedHashMap.java b/src/java.base/share/classes/java/util/LinkedHashMap.java
index 1984043f0d0b3..d4052f396bfa9 100644
--- a/src/java.base/share/classes/java/util/LinkedHashMap.java
+++ b/src/java.base/share/classes/java/util/LinkedHashMap.java
@@ -29,18 +29,23 @@
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
import java.io.IOException;
+import java.util.function.Function;
/**
* Hash table and linked list implementation of the {@code Map} interface,
- * with predictable iteration order. This implementation differs from
- * {@code HashMap} in that it maintains a doubly-linked list running through
- * all of its entries. This linked list defines the iteration ordering,
+ * with well-defined encounter order. This implementation differs from
+ * {@code HashMap} in that it maintains a doubly-linked list running through all of
+ * its entries. This linked list defines the encounter order (the order of iteration),
* which is normally the order in which keys were inserted into the map
- * (insertion-order). Note that insertion order is not affected
- * if a key is re-inserted into the map. (A key {@code k} is
- * reinserted into a map {@code m} if {@code m.put(k, v)} is invoked when
+ * (insertion-order). The least recently inserted entry (the eldest) is
+ * first, and the youngest entry is last. Note that encounter order is not affected
+ * if a key is re-inserted into the map with the {@code put} method. (A key
+ * {@code k} is reinserted into a map {@code m} if {@code m.put(k, v)} is invoked when
* {@code m.containsKey(k)} would return {@code true} immediately prior to
- * the invocation.)
+ * the invocation.) The reverse-ordered view of this map is in the opposite order, with
+ * the youngest entry appearing first and the eldest entry appearing last.
+ * The encounter order of entries already in the map can be changed by using
+ * the {@link #putFirst putFirst} and {@link #putLast putLast} methods.
*
*
This implementation spares its clients from the unspecified, generally
* chaotic ordering provided by {@link HashMap} (and {@link Hashtable}),
@@ -59,7 +64,7 @@
* order they were presented.)
*
*
A special {@link #LinkedHashMap(int,float,boolean) constructor} is
- * provided to create a linked hash map whose order of iteration is the order
+ * provided to create a linked hash map whose encounter order is the order
* in which its entries were last accessed, from least-recently accessed to
* most-recently (access-order). This kind of map is well-suited to
* building LRU caches. Invoking the {@code put}, {@code putIfAbsent},
@@ -70,16 +75,24 @@
* of the entry if the value is replaced. The {@code putAll} method generates one
* entry access for each mapping in the specified map, in the order that
* key-value mappings are provided by the specified map's entry set iterator.
- * No other methods generate entry accesses. In particular, operations
- * on collection-views do not affect the order of iteration of the
- * backing map.
+ * No other methods generate entry accesses. Invoking these methods on the
+ * reversed view generates accesses to entries on the backing map. Note that in the
+ * reversed view, an access to an entry moves it first in encounter order.
+ * Explicit-positioning methods such as {@code putFirst} or {@code lastEntry}, whether on
+ * the map or on its reverse-ordered view, perform the positioning operation and
+ * do not generate entry accesses. Operations on the {@code keySet}, {@code values},
+ * and {@code entrySet} views or on their sequenced counterparts do not affect
+ * the encounter order of the backing map.
*
*
The {@link #removeEldestEntry(Map.Entry)} method may be overridden to
* impose a policy for removing stale mappings automatically when new mappings
- * are added to the map.
+ * are added to the map. Alternatively, since the "eldest" entry is the first
+ * entry in encounter order, programs can inspect and remove stale mappings through
+ * use of the {@link #firstEntry firstEntry} and {@link #pollFirstEntry pollFirstEntry}
+ * methods.
*
- *
This class provides all of the optional {@code Map} operations, and
- * permits null elements. Like {@code HashMap}, it provides constant-time
+ *
This class provides all of the optional {@code Map} and {@code SequencedMap} operations,
+ * and it permits null elements. Like {@code HashMap}, it provides constant-time
* performance for the basic operations ({@code add}, {@code contains} and
* {@code remove}), assuming the hash function disperses elements
* properly among the buckets. Performance is likely to be just slightly
@@ -162,7 +175,7 @@
*/
public class LinkedHashMap
extends HashMap
- implements Map
+ implements SequencedMap
{
/*
@@ -220,14 +233,25 @@ static class Entry extends HashMap.Node {
// internal utilities
// link at the end of list
- private void linkNodeLast(LinkedHashMap.Entry p) {
- LinkedHashMap.Entry last = tail;
- tail = p;
- if (last == null)
+ private void linkNodeAtEnd(LinkedHashMap.Entry p) {
+ if (putMode == PUT_FIRST) {
+ LinkedHashMap.Entry first = head;
head = p;
- else {
- p.before = last;
- last.after = p;
+ if (first == null)
+ tail = p;
+ else {
+ p.after = first;
+ first.before = p;
+ }
+ } else {
+ LinkedHashMap.Entry last = tail;
+ tail = p;
+ if (last == null)
+ head = p;
+ else {
+ p.before = last;
+ last.after = p;
+ }
}
}
@@ -256,7 +280,7 @@ void reinitialize() {
Node newNode(int hash, K key, V value, Node e) {
LinkedHashMap.Entry p =
new LinkedHashMap.Entry<>(hash, key, value, e);
- linkNodeLast(p);
+ linkNodeAtEnd(p);
return p;
}
@@ -270,7 +294,7 @@ Node replacementNode(Node p, Node next) {
TreeNode newTreeNode(int hash, K key, V value, Node next) {
TreeNode p = new TreeNode<>(hash, key, value, next);
- linkNodeLast(p);
+ linkNodeAtEnd(p);
return p;
}
@@ -303,9 +327,17 @@ void afterNodeInsertion(boolean evict) { // possibly remove eldest
}
}
- void afterNodeAccess(Node e) { // move node to last
+ static final int PUT_NORM = 0;
+ static final int PUT_FIRST = 1;
+ static final int PUT_LAST = 2;
+ int putMode = PUT_NORM;
+
+ // Called after update, but not after insertion
+ void afterNodeAccess(Node e) {
LinkedHashMap.Entry last;
- if (accessOrder && (last = tail) != e) {
+ LinkedHashMap.Entry first;
+ if ((putMode == PUT_LAST || (putMode == PUT_NORM && accessOrder)) && (last = tail) != e) {
+ // move node to last
LinkedHashMap.Entry p =
(LinkedHashMap.Entry)e, b = p.before, a = p.after;
p.after = null;
@@ -325,6 +357,61 @@ void afterNodeAccess(Node e) { // move node to last
}
tail = p;
++modCount;
+ } else if (putMode == PUT_FIRST && (first = head) != e) {
+ // move node to first
+ LinkedHashMap.Entry p =
+ (LinkedHashMap.Entry)e, b = p.before, a = p.after;
+ p.before = null;
+ if (a == null)
+ tail = b;
+ else
+ a.before = b;
+ if (b != null)
+ b.after = a;
+ else
+ first = a;
+ if (first == null)
+ tail = p;
+ else {
+ p.after = first;
+ first.before = p;
+ }
+ head = p;
+ ++modCount;
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * If this map already contains a mapping for this key, the mapping is relocated if necessary
+ * so that it is first in encounter order.
+ *
+ * @since 21
+ */
+ public V putFirst(K k, V v) {
+ try {
+ putMode = PUT_FIRST;
+ return this.put(k, v);
+ } finally {
+ putMode = PUT_NORM;
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * If this map already contains a mapping for this key, the mapping is relocated if necessary
+ * so that it is last in encounter order.
+ *
+ * @since 21
+ */
+ public V putLast(K k, V v) {
+ try {
+ putMode = PUT_LAST;
+ return this.put(k, v);
+ } finally {
+ putMode = PUT_NORM;
}
}
@@ -519,8 +606,9 @@ protected boolean removeEldestEntry(Map.Entry eldest) {
}
/**
- * Returns a {@link Set} view of the keys contained in this map.
- * The set is backed by the map, so changes to the map are
+ * Returns a {@link Set} view of the keys contained in this map. The encounter
+ * order of the keys in the view matches the encounter order of mappings of
+ * this map. The set is backed by the map, so changes to the map are
* reflected in the set, and vice-versa. If the map is modified
* while an iteration over the set is in progress (except through
* the iterator's own {@code remove} operation), the results of
@@ -537,39 +625,79 @@ protected boolean removeEldestEntry(Map.Entry eldest) {
* @return a set view of the keys contained in this map
*/
public Set keySet() {
+ return sequencedKeySet();
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * The returned view has the same characteristics as specified for the view
+ * returned by the {@link #keySet keySet} method.
+ *
+ * @return {@inheritDoc}
+ * @since 21
+ */
+ public SequencedSet sequencedKeySet() {
Set ks = keySet;
if (ks == null) {
- ks = new LinkedKeySet();
- keySet = ks;
+ SequencedSet sks = new LinkedKeySet(false);
+ keySet = sks;
+ return sks;
+ } else {
+ // The cast should never fail, since the only assignment of non-null to keySet is
+ // above, and assignments in AbstractMap and HashMap are in overridden methods.
+ return (SequencedSet) ks;
}
- return ks;
}
- @Override
+ static Node nsee(Node node) {
+ if (node == null)
+ throw new NoSuchElementException();
+ else
+ return node;
+ }
+
final T[] keysToArray(T[] a) {
+ return keysToArray(a, false);
+ }
+
+ final T[] keysToArray(T[] a, boolean reversed) {
Object[] r = a;
int idx = 0;
- for (LinkedHashMap.Entry e = head; e != null; e = e.after) {
- r[idx++] = e.key;
+ if (reversed) {
+ for (LinkedHashMap.Entry e = tail; e != null; e = e.before) {
+ r[idx++] = e.key;
+ }
+ } else {
+ for (LinkedHashMap.Entry e = head; e != null; e = e.after) {
+ r[idx++] = e.key;
+ }
}
return a;
}
- @Override
- final T[] valuesToArray(T[] a) {
+ final T[] valuesToArray(T[] a, boolean reversed) {
Object[] r = a;
int idx = 0;
- for (LinkedHashMap.Entry e = head; e != null; e = e.after) {
- r[idx++] = e.value;
+ if (reversed) {
+ for (LinkedHashMap.Entry e = tail; e != null; e = e.before) {
+ r[idx++] = e.value;
+ }
+ } else {
+ for (LinkedHashMap.Entry e = head; e != null; e = e.after) {
+ r[idx++] = e.value;
+ }
}
return a;
}
- final class LinkedKeySet extends AbstractSet {
+ final class LinkedKeySet extends AbstractSet implements SequencedSet {
+ final boolean reversed;
+ LinkedKeySet(boolean reversed) { this.reversed = reversed; }
public final int size() { return size; }
public final void clear() { LinkedHashMap.this.clear(); }
public final Iterator iterator() {
- return new LinkedKeyIterator();
+ return new LinkedKeyIterator(reversed);
}
public final boolean contains(Object o) { return containsKey(o); }
public final boolean remove(Object key) {
@@ -582,27 +710,54 @@ public final Spliterator spliterator() {
}
public Object[] toArray() {
- return keysToArray(new Object[size]);
+ return keysToArray(new Object[size], reversed);
}
public T[] toArray(T[] a) {
- return keysToArray(prepareArray(a));
+ return keysToArray(prepareArray(a), reversed);
}
public final void forEach(Consumer super K> action) {
if (action == null)
throw new NullPointerException();
int mc = modCount;
- for (LinkedHashMap.Entry e = head; e != null; e = e.after)
- action.accept(e.key);
+ if (reversed) {
+ for (LinkedHashMap.Entry e = tail; e != null; e = e.before)
+ action.accept(e.key);
+ } else {
+ for (LinkedHashMap.Entry e = head; e != null; e = e.after)
+ action.accept(e.key);
+ }
if (modCount != mc)
throw new ConcurrentModificationException();
}
+ public final void addFirst(K k) { throw new UnsupportedOperationException(); }
+ public final void addLast(K k) { throw new UnsupportedOperationException(); }
+ public final K getFirst() { return nsee(reversed ? tail : head).key; }
+ public final K getLast() { return nsee(reversed ? head : tail).key; }
+ public final K removeFirst() {
+ var node = nsee(reversed ? tail : head);
+ removeNode(node.hash, node.key, null, false, false);
+ return node.key;
+ }
+ public final K removeLast() {
+ var node = nsee(reversed ? head : tail);
+ removeNode(node.hash, node.key, null, false, false);
+ return node.key;
+ }
+ public SequencedSet reversed() {
+ if (reversed) {
+ return LinkedHashMap.this.sequencedKeySet();
+ } else {
+ return new LinkedKeySet(true);
+ }
+ }
}
/**
- * Returns a {@link Collection} view of the values contained in this map.
- * The collection is backed by the map, so changes to the map are
+ * Returns a {@link Collection} view of the values contained in this map. The
+ * encounter order of values in the view matches the encounter order of entries in
+ * this map. The collection is backed by the map, so changes to the map are
* reflected in the collection, and vice-versa. If the map is
* modified while an iteration over the collection is in progress
* (except through the iterator's own {@code remove} operation),
@@ -619,19 +774,38 @@ public final void forEach(Consumer super K> action) {
* @return a view of the values contained in this map
*/
public Collection values() {
+ return sequencedValues();
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * The returned view has the same characteristics as specified for the view
+ * returned by the {@link #values values} method.
+ *
+ * @return {@inheritDoc}
+ * @since 21
+ */
+ public SequencedCollection sequencedValues() {
Collection vs = values;
if (vs == null) {
- vs = new LinkedValues();
- values = vs;
+ SequencedCollection svs = new LinkedValues(false);
+ values = svs;
+ return svs;
+ } else {
+ // The cast should never fail, since the only assignment of non-null to values is
+ // above, and assignments in AbstractMap and HashMap are in overridden methods.
+ return (SequencedCollection) vs;
}
- return vs;
}
- final class LinkedValues extends AbstractCollection {
+ final class LinkedValues extends AbstractCollection implements SequencedCollection {
+ final boolean reversed;
+ LinkedValues(boolean reversed) { this.reversed = reversed; }
public final int size() { return size; }
public final void clear() { LinkedHashMap.this.clear(); }
public final Iterator iterator() {
- return new LinkedValueIterator();
+ return new LinkedValueIterator(reversed);
}
public final boolean contains(Object o) { return containsValue(o); }
public final Spliterator spliterator() {
@@ -640,26 +814,53 @@ public final Spliterator spliterator() {
}
public Object[] toArray() {
- return valuesToArray(new Object[size]);
+ return valuesToArray(new Object[size], reversed);
}
public T[] toArray(T[] a) {
- return valuesToArray(prepareArray(a));
+ return valuesToArray(prepareArray(a), reversed);
}
public final void forEach(Consumer super V> action) {
if (action == null)
throw new NullPointerException();
int mc = modCount;
- for (LinkedHashMap.Entry e = head; e != null; e = e.after)
- action.accept(e.value);
+ if (reversed) {
+ for (LinkedHashMap.Entry e = tail; e != null; e = e.before)
+ action.accept(e.value);
+ } else {
+ for (LinkedHashMap.Entry e = head; e != null; e = e.after)
+ action.accept(e.value);
+ }
if (modCount != mc)
throw new ConcurrentModificationException();
}
+ public final void addFirst(V v) { throw new UnsupportedOperationException(); }
+ public final void addLast(V v) { throw new UnsupportedOperationException(); }
+ public final V getFirst() { return nsee(reversed ? tail : head).value; }
+ public final V getLast() { return nsee(reversed ? head : tail).value; }
+ public final V removeFirst() {
+ var node = nsee(reversed ? tail : head);
+ removeNode(node.hash, node.key, null, false, false);
+ return node.value;
+ }
+ public final V removeLast() {
+ var node = nsee(reversed ? head : tail);
+ removeNode(node.hash, node.key, null, false, false);
+ return node.value;
+ }
+ public SequencedCollection reversed() {
+ if (reversed) {
+ return LinkedHashMap.this.sequencedValues();
+ } else {
+ return new LinkedValues(true);
+ }
+ }
}
/**
- * Returns a {@link Set} view of the mappings contained in this map.
+ * Returns a {@link Set} view of the mappings contained in this map. The encounter
+ * order of the view matches the encounter order of entries of this map.
* The set is backed by the map, so changes to the map are
* reflected in the set, and vice-versa. If the map is modified
* while an iteration over the set is in progress (except through
@@ -678,15 +879,39 @@ public final void forEach(Consumer super V> action) {
* @return a set view of the mappings contained in this map
*/
public Set> entrySet() {
- Set> es;
- return (es = entrySet) == null ? (entrySet = new LinkedEntrySet()) : es;
+ return sequencedEntrySet();
}
- final class LinkedEntrySet extends AbstractSet> {
+ /**
+ * {@inheritDoc}
+ *
+ * The returned view has the same characteristics as specified for the view
+ * returned by the {@link #entrySet entrySet} method.
+ *
+ * @return {@inheritDoc}
+ * @since 21
+ */
+ public SequencedSet> sequencedEntrySet() {
+ Set> es = entrySet;
+ if (es == null) {
+ SequencedSet> ses = new LinkedEntrySet(false);
+ entrySet = ses;
+ return ses;
+ } else {
+ // The cast should never fail, since the only assignment of non-null to entrySet is
+ // above, and assignments in HashMap are in overridden methods.
+ return (SequencedSet>) es;
+ }
+ }
+
+ final class LinkedEntrySet extends AbstractSet>
+ implements SequencedSet> {
+ final boolean reversed;
+ LinkedEntrySet(boolean reversed) { this.reversed = reversed; }
public final int size() { return size; }
public final void clear() { LinkedHashMap.this.clear(); }
public final Iterator> iterator() {
- return new LinkedEntryIterator();
+ return new LinkedEntryIterator(reversed);
}
public final boolean contains(Object o) {
if (!(o instanceof Map.Entry, ?> e))
@@ -712,11 +937,43 @@ public final void forEach(Consumer super Map.Entry> action) {
if (action == null)
throw new NullPointerException();
int mc = modCount;
- for (LinkedHashMap.Entry e = head; e != null; e = e.after)
- action.accept(e);
+ if (reversed) {
+ for (LinkedHashMap.Entry e = tail; e != null; e = e.before)
+ action.accept(e);
+ } else {
+ for (LinkedHashMap.Entry e = head; e != null; e = e.after)
+ action.accept(e);
+ }
if (modCount != mc)
throw new ConcurrentModificationException();
}
+ final Node nsee(Node e) {
+ if (e == null)
+ throw new NoSuchElementException();
+ else
+ return e;
+ }
+ public final void addFirst(Map.Entry e) { throw new UnsupportedOperationException(); }
+ public final void addLast(Map.Entry e) { throw new UnsupportedOperationException(); }
+ public final Map.Entry getFirst() { return nsee(reversed ? tail : head); }
+ public final Map.Entry getLast() { return nsee(reversed ? head : tail); }
+ public final Map.Entry removeFirst() {
+ var node = nsee(reversed ? tail : head);
+ removeNode(node.hash, node.key, null, false, false);
+ return node;
+ }
+ public final Map.Entry removeLast() {
+ var node = nsee(reversed ? head : tail);
+ removeNode(node.hash, node.key, null, false, false);
+ return node;
+ }
+ public SequencedSet> reversed() {
+ if (reversed) {
+ return LinkedHashMap.this.sequencedEntrySet();
+ } else {
+ return new LinkedEntrySet(true);
+ }
+ }
}
// Map overrides
@@ -747,9 +1004,11 @@ abstract class LinkedHashIterator {
LinkedHashMap.Entry next;
LinkedHashMap.Entry current;
int expectedModCount;
+ boolean reversed;
- LinkedHashIterator() {
- next = head;
+ LinkedHashIterator(boolean reversed) {
+ this.reversed = reversed;
+ next = reversed ? tail : head;
expectedModCount = modCount;
current = null;
}
@@ -765,7 +1024,7 @@ final LinkedHashMap.Entry nextNode() {
if (e == null)
throw new NoSuchElementException();
current = e;
- next = e.after;
+ next = reversed ? e.before : e.after;
return e;
}
@@ -783,16 +1042,19 @@ public final void remove() {
final class LinkedKeyIterator extends LinkedHashIterator
implements Iterator {
+ LinkedKeyIterator(boolean reversed) { super(reversed); }
public final K next() { return nextNode().getKey(); }
}
final class LinkedValueIterator extends LinkedHashIterator
implements Iterator {
+ LinkedValueIterator(boolean reversed) { super(reversed); }
public final V next() { return nextNode().value; }
}
final class LinkedEntryIterator extends LinkedHashIterator
implements Iterator> {
+ LinkedEntryIterator(boolean reversed) { super(reversed); }
public final Map.Entry next() { return nextNode(); }
}
@@ -816,4 +1078,175 @@ public static LinkedHashMap newLinkedHashMap(int numMappings) {
return new LinkedHashMap<>(HashMap.calculateHashMapCapacity(numMappings));
}
+ // Reversed View
+
+ /**
+ * {@inheritDoc}
+ *
+ * Modifications to the reversed view and its map views are permitted and will be
+ * propagated to this map. In addition, modifications to this map will be visible
+ * in the reversed view and its map views.
+ *
+ * @return {@inheritDoc}
+ * @since 21
+ */
+ public SequencedMap reversed() {
+ return new ReversedLinkedHashMapView<>(this);
+ }
+
+ static class ReversedLinkedHashMapView extends AbstractMap
+ implements SequencedMap {
+ final LinkedHashMap base;
+
+ ReversedLinkedHashMapView(LinkedHashMap lhm) {
+ base = lhm;
+ }
+
+ // Object
+ // inherit toString() from AbstractMap; it depends on entrySet()
+
+ public boolean equals(Object o) {
+ return base.equals(o);
+ }
+
+ public int hashCode() {
+ return base.hashCode();
+ }
+
+ // Map
+
+ public int size() {
+ return base.size();
+ }
+
+ public boolean isEmpty() {
+ return base.isEmpty();
+ }
+
+ public boolean containsKey(Object key) {
+ return base.containsKey(key);
+ }
+
+ public boolean containsValue(Object value) {
+ return base.containsValue(value);
+ }
+
+ public V get(Object key) {
+ return base.get(key);
+ }
+
+ public V put(K key, V value) {
+ return base.put(key, value);
+ }
+
+ public V remove(Object key) {
+ return base.remove(key);
+ }
+
+ public void putAll(Map extends K, ? extends V> m) {
+ base.putAll(m);
+ }
+
+ public void clear() {
+ base.clear();
+ }
+
+ public Set keySet() {
+ return base.sequencedKeySet().reversed();
+ }
+
+ public Collection values() {
+ return base.sequencedValues().reversed();
+ }
+
+ public Set> entrySet() {
+ return base.sequencedEntrySet().reversed();
+ }
+
+ public V getOrDefault(Object key, V defaultValue) {
+ return base.getOrDefault(key, defaultValue);
+ }
+
+ public void forEach(BiConsumer super K, ? super V> action) {
+ if (action == null)
+ throw new NullPointerException();
+ int mc = base.modCount;
+ for (LinkedHashMap.Entry e = base.tail; e != null; e = e.before)
+ action.accept(e.key, e.value);
+ if (base.modCount != mc)
+ throw new ConcurrentModificationException();
+ }
+
+ public void replaceAll(BiFunction super K, ? super V, ? extends V> function) {
+ if (function == null)
+ throw new NullPointerException();
+ int mc = base.modCount;
+ for (LinkedHashMap.Entry e = base.tail; e != null; e = e.before)
+ e.value = function.apply(e.key, e.value);
+ if (base.modCount != mc)
+ throw new ConcurrentModificationException();
+ }
+
+ public V putIfAbsent(K key, V value) {
+ return base.putIfAbsent(key, value);
+ }
+
+ public boolean remove(Object key, Object value) {
+ return base.remove(key, value);
+ }
+
+ public boolean replace(K key, V oldValue, V newValue) {
+ return base.replace(key, oldValue, newValue);
+ }
+
+ public V replace(K key, V value) {
+ return base.replace(key, value);
+ }
+
+ public V computeIfAbsent(K key, Function super K, ? extends V> mappingFunction) {
+ return base.computeIfAbsent(key, mappingFunction);
+ }
+
+ public V computeIfPresent(K key, BiFunction super K, ? super V, ? extends V> remappingFunction) {
+ return base.computeIfPresent(key, remappingFunction);
+ }
+
+ public V compute(K key, BiFunction super K, ? super V, ? extends V> remappingFunction) {
+ return base.compute(key, remappingFunction);
+ }
+
+ public V merge(K key, V value, BiFunction super V, ? super V, ? extends V> remappingFunction) {
+ return base.merge(key, value, remappingFunction);
+ }
+
+ // SequencedMap
+
+ public SequencedMap reversed() {
+ return base;
+ }
+
+ public Entry firstEntry() {
+ return base.lastEntry();
+ }
+
+ public Entry lastEntry() {
+ return base.firstEntry();
+ }
+
+ public Entry pollFirstEntry() {
+ return base.pollLastEntry();
+ }
+
+ public Entry pollLastEntry() {
+ return base.pollFirstEntry();
+ }
+
+ public V putFirst(K k, V v) {
+ return base.putLast(k, v);
+ }
+
+ public V putLast(K k, V v) {
+ return base.putFirst(k, v);
+ }
+ }
}
diff --git a/src/java.base/share/classes/java/util/LinkedHashSet.java b/src/java.base/share/classes/java/util/LinkedHashSet.java
index 330e37f8be3e0..fd9b52e258915 100644
--- a/src/java.base/share/classes/java/util/LinkedHashSet.java
+++ b/src/java.base/share/classes/java/util/LinkedHashSet.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2000, 2022, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2000, 2023, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -27,15 +27,19 @@
/**
* Hash table and linked list implementation of the {@code Set} interface,
- * with predictable iteration order. This implementation differs from
+ * with well-defined encounter order. This implementation differs from
* {@code HashSet} in that it maintains a doubly-linked list running through
- * all of its entries. This linked list defines the iteration ordering,
- * which is the order in which elements were inserted into the set
- * (insertion-order). Note that insertion order is not affected
- * if an element is re-inserted into the set. (An element {@code e}
- * is reinserted into a set {@code s} if {@code s.add(e)} is invoked when
- * {@code s.contains(e)} would return {@code true} immediately prior to
- * the invocation.)
+ * all of its entries. This linked list defines the encounter order (iteration
+ * order), which is the order in which elements were inserted into the set
+ * (insertion-order). The least recently inserted element (the eldest) is
+ * first, and the youngest element is last. Note that encounter order is not affected
+ * if an element is re-inserted into the set with the {@code add} method.
+ * (An element {@code e} is reinserted into a set {@code s} if {@code s.add(e)} is
+ * invoked when {@code s.contains(e)} would return {@code true} immediately prior to
+ * the invocation.) The reverse-ordered view of this set is in the opposite order, with
+ * the youngest element appearing first and the eldest element appearing last. The encounter
+ * order of elements already in the set can be changed by using the
+ * {@link #addFirst addFirst} and {@link #addLast addLast} methods.
*
*
This implementation spares its clients from the unspecified, generally
* chaotic ordering provided by {@link HashSet}, without incurring the
@@ -53,8 +57,8 @@
* the copy. (Clients generally appreciate having things returned in the same
* order they were presented.)
*
- *
This class provides all of the optional {@code Set} operations, and
- * permits null elements. Like {@code HashSet}, it provides constant-time
+ *
This class provides all of the optional {@link Set} and {@link SequencedSet}
+ * operations, and it permits null elements. Like {@code HashSet}, it provides constant-time
* performance for the basic operations ({@code add}, {@code contains} and
* {@code remove}), assuming the hash function disperses elements
* properly among the buckets. Performance is likely to be just slightly
@@ -117,7 +121,7 @@
public class LinkedHashSet
extends HashSet
- implements Set, Cloneable, java.io.Serializable {
+ implements SequencedSet, Cloneable, java.io.Serializable {
@java.io.Serial
private static final long serialVersionUID = -2851667679971038690L;
@@ -221,4 +225,100 @@ public static LinkedHashSet newLinkedHashSet(int numElements) {
return new LinkedHashSet<>(HashMap.calculateHashMapCapacity(numElements));
}
+ @SuppressWarnings("unchecked")
+ LinkedHashMap map() {
+ return (LinkedHashMap) map;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * If this set already contains the element, it is relocated if necessary so that it is
+ * first in encounter order.
+ *
+ * @since 21
+ */
+ public void addFirst(E e) {
+ map().putFirst(e, PRESENT);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * If this set already contains the element, it is relocated if necessary so that it is
+ * last in encounter order.
+ *
+ * @since 21
+ */
+ public void addLast(E e) {
+ map().putLast(e, PRESENT);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @throws NoSuchElementException {@inheritDoc}
+ * @since 21
+ */
+ public E getFirst() {
+ return map().sequencedKeySet().getFirst();
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @throws NoSuchElementException {@inheritDoc}
+ * @since 21
+ */
+ public E getLast() {
+ return map().sequencedKeySet().getLast();
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @throws NoSuchElementException {@inheritDoc}
+ * @since 21
+ */
+ public E removeFirst() {
+ return map().sequencedKeySet().removeFirst();
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @throws NoSuchElementException {@inheritDoc}
+ * @since 21
+ */
+ public E removeLast() {
+ return map().sequencedKeySet().removeLast();
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * Modifications to the reversed view are permitted and will be propagated to this set.
+ * In addition, modifications to this set will be visible in the reversed view.
+ *
+ * @return {@inheritDoc}
+ * @since 21
+ */
+ public SequencedSet reversed() {
+ class ReverseLinkedHashSetView extends AbstractSet implements SequencedSet {
+ public int size() { return LinkedHashSet.this.size(); }
+ public Iterator iterator() { return map().sequencedKeySet().reversed().iterator(); }
+ public boolean add(E e) { return LinkedHashSet.this.add(e); }
+ public void addFirst(E e) { LinkedHashSet.this.addLast(e); }
+ public void addLast(E e) { LinkedHashSet.this.addFirst(e); }
+ public E getFirst() { return LinkedHashSet.this.getLast(); }
+ public E getLast() { return LinkedHashSet.this.getFirst(); }
+ public E removeFirst() { return LinkedHashSet.this.removeLast(); }
+ public E removeLast() { return LinkedHashSet.this.removeFirst(); }
+ public SequencedSet reversed() { return LinkedHashSet.this; }
+ public Object[] toArray() { return map().keysToArray(new Object[map.size()], true); }
+ public T[] toArray(T[] a) { return map().keysToArray(map.prepareArray(a), true); }
+ }
+
+ return new ReverseLinkedHashSetView();
+ }
}
diff --git a/src/java.base/share/classes/java/util/LinkedList.java b/src/java.base/share/classes/java/util/LinkedList.java
index a504b4e83907c..d7c67c9862839 100644
--- a/src/java.base/share/classes/java/util/LinkedList.java
+++ b/src/java.base/share/classes/java/util/LinkedList.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1997, 2019, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2023, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -25,7 +25,14 @@
package java.util;
+import java.io.IOException;
+import java.io.ObjectInput;
+import java.io.ObjectOutput;
import java.util.function.Consumer;
+import java.util.function.IntFunction;
+import java.util.function.Predicate;
+import java.util.function.UnaryOperator;
+import java.util.stream.Stream;
/**
* Doubly-linked list implementation of the {@code List} and {@code Deque}
@@ -1266,4 +1273,267 @@ public int characteristics() {
}
}
+ /**
+ * {@inheritDoc}
+ *
+ * Modifications to the reversed view are permitted and will be propagated to this list.
+ * In addition, modifications to this list will be visible in the reversed view.
+ *
+ * @return {@inheritDoc}
+ * @since 21
+ */
+ public LinkedList reversed() {
+ return new ReverseOrderLinkedListView<>(this, super.reversed(), Deque.super.reversed());
+ }
+
+ // all operations are delegated to the reverse-ordered views.
+ // TODO audit all overridden methods
+ @SuppressWarnings("serial")
+ static class ReverseOrderLinkedListView extends LinkedList implements java.io.Externalizable {
+ final LinkedList list;
+ final List