diff --git a/src/java.base/share/classes/java/util/AbstractMap.java b/src/java.base/share/classes/java/util/AbstractMap.java
index 8e9414f8ef5..b0d311f6565 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 abstract class AbstractMap<K,V> implements Map<K,V> {
         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.
+     * <p>
+     * Ideally this would be a private class within SequencedMap, but private
+     * classes aren't permitted within interfaces.
+     *
+     * @param <E> the view's element type
+     */
+    /* non-public */ abstract static class ViewCollection<E> implements Collection<E> {
+        UnsupportedOperationException uoe() { return new UnsupportedOperationException(); }
+        final Collection<E> view;
+
+        ViewCollection(Collection<E> 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<E> iterator() { return view.iterator(); }
+        public Stream<E> 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<E> spliterator() { return view.spliterator(); }
+        public Stream<E> stream() { return view.stream(); }
+        public Object[] toArray() { return view.toArray(); }
+        public <T> T[] toArray(IntFunction<T[]> generator) { return view.toArray(generator); }
+        public <T> 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 603ebceb757..eb459b80517 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 @@ import jdk.internal.access.SharedSecrets;
  * exception for its correctness: <i>the fail-fast behavior of iterators
  * should be used only to detect bugs.</i>
  *
- * <p>This class and its iterator implement all of the
- * <em>optional</em> methods of the {@link Collection} and {@link
- * Iterator} interfaces.
+ * <p>This class and its iterator implement all of the <em>optional</em> methods of the
+ * {@link Collection}, {@link SequencedCollection}, and {@link Iterator} interfaces.
  *
  * <p>This class is a member of the
  * <a href="{@docRoot}/java.base/java/util/package-summary.html#CollectionsFramework">
diff --git a/src/java.base/share/classes/java/util/ArrayList.java b/src/java.base/share/classes/java/util/ArrayList.java
index a36dcd8a796..1f27572ef16 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 class ArrayList<E> extends AbstractList<E>
         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 class ArrayList<E> extends AbstractList<E>
         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 class ArrayList<E> extends AbstractList<E>
         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 fbaa1881a89..0f63f0b26a4 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 @@ import java.util.stream.StreamSupport;
 /**
  * The root interface in the <i>collection hierarchy</i>.  A collection
  * represents a group of objects, known as its <i>elements</i>.  Some
- * collections allow duplicate elements and others do not.  Some are ordered
- * and others unordered.  The JDK does not provide any <i>direct</i>
+ * collections allow duplicate elements and others do not.  Some are ordered,
+ * and others are unordered. Collections that have a defined
+ * <a href="SequencedCollection.html#encounter">encounter order</a>
+ * are generally subtypes of the {@link SequencedCollection} interface.
+ * The JDK does not provide any <i>direct</i>
  * 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 @@ import java.util.stream.StreamSupport;
  * 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 &mdash; if changes
  * are permitted &mdash; are written through to the backing collection.
@@ -202,7 +206,8 @@ import java.util.stream.StreamSupport;
  * 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 <a href="#view">view collections</a> are not serializable.
+ * collections. In particular, many <a href="#view">view collections</a> are not serializable,
+ * even if the original collection is serializable.
  *
  * <p>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 @@ public interface Collection<E> extends Iterable<E> {
      * 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
+     * <a href="SequencedCollection.html#encounter">encounter order</a>,
+     * 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 4425b4483c0..f45d297f300 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 @@ public class Collections {
      *
      * 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 class Collections {
         }
     }
 
+    /**
+     * Returns an <a href="Collection.html#unmodview">unmodifiable view</a> 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}.<p>
+     *
+     * The returned collection does <i>not</i> 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.<p>
+     *
+     * 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  <T> 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 <T> SequencedCollection<T> unmodifiableSequencedCollection(SequencedCollection<? extends T> c) {
+        if (c.getClass() == UnmodifiableSequencedCollection.class) {
+            return (SequencedCollection<T>) c;
+        }
+        return new UnmodifiableSequencedCollection<>(c);
+    }
+
+    /**
+     * @serial include
+     */
+    static class UnmodifiableSequencedCollection<E> extends UnmodifiableCollection<E>
+            implements SequencedCollection<E>, Serializable {
+
+        @java.io.Serial
+        private static final long serialVersionUID = -6060065079711684830L;
+
+        UnmodifiableSequencedCollection(SequencedCollection<? extends E> c) {
+            super(c);
+        }
+
+        @SuppressWarnings("unchecked")
+        private SequencedCollection<E> sc() {
+            return (SequencedCollection<E>) 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<E> 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 <a href="Collection.html#unmodview">unmodifiable view</a> of the
      * specified set. Query operations on the returned set "read through" to the specified
@@ -1166,6 +1253,56 @@ public class Collections {
         public int hashCode()           {return c.hashCode();}
     }
 
+    /**
+     * Returns an <a href="Collection.html#unmodview">unmodifiable view</a> 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}.<p>
+     *
+     * 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  <T> 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 <T> SequencedSet<T> unmodifiableSequencedSet(SequencedSet<? extends T> s) {
+        // Not checking for subclasses because of heap pollution and information leakage.
+        if (s.getClass() == UnmodifiableSequencedSet.class) {
+            return (SequencedSet<T>) s;
+        }
+        return new UnmodifiableSequencedSet<>(s);
+    }
+
+    /**
+     * @serial include
+     */
+    static class UnmodifiableSequencedSet<E> extends UnmodifiableSequencedCollection<E>
+                                             implements SequencedSet<E>, 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<E> ss() {
+            return (SequencedSet<E>) 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<E> reversed() {
+            return new UnmodifiableSequencedSet<>(ss().reversed());
+        }
+    }
+
     /**
      * Returns an <a href="Collection.html#unmodview">unmodifiable view</a> of the
      * specified sorted set. Query operations on the returned sorted set "read
@@ -1504,7 +1641,7 @@ public class Collections {
         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 @@ public class Collections {
         }
     }
 
+    /**
+     * Returns an <a href="Collection.html#unmodview">unmodifiable view</a> 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}.<p>
+     *
+     * 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 <K> the class of the map keys
+     * @param <V> 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 <K,V> SequencedMap<K,V> 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<K,V>) m;
+        }
+        return new UnmodifiableSequencedMap<>(m);
+    }
+
+    /**
+     * @serial include
+     */
+    private static class UnmodifiableSequencedMap<K,V> extends UnmodifiableMap<K,V> implements SequencedMap<K,V>, Serializable {
+        @java.io.Serial
+        private static final long serialVersionUID = -8171676257373950636L;
+
+        UnmodifiableSequencedMap(Map<? extends K, ? extends V> m) {
+            super(m);
+        }
+
+        @SuppressWarnings("unchecked")
+        private SequencedMap<K, V> sm() {
+            return (SequencedMap<K, V>) 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<K, V> reversed() {
+            return new UnmodifiableSequencedMap<>(sm().reversed());
+        }
+
+        public Entry<K, V> pollFirstEntry() {
+            throw new UnsupportedOperationException();
+        }
+
+        public Entry<K, V> 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 <a href="Collection.html#unmodview">unmodifiable view</a> of the
      * specified sorted map. Query operations on the returned sorted map "read through"
@@ -5326,6 +5529,14 @@ public class Collections {
      *
      * 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 <i>view</i> of a
+     * sequenced collection, use the {@link SequencedCollection#reversed
+     * SequencedCollection.reversed} method. Or, to obtain a reverse-ordered
+     * <i>view</i> of a sequenced map, use the {@link SequencedMap#reversed
+     * SequencedMap.reversed} method.
+     *
      * @param  <T> the class of the objects compared by the comparator
      * @return A comparator that imposes the reverse of the <i>natural
      *         ordering</i> on a collection of objects that implement
@@ -5372,6 +5583,14 @@ public class Collections {
      * <p>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 <i>view</i> of a
+     * sequenced collection, use the {@link SequencedCollection#reversed
+     * SequencedCollection.reversed} method. Or, to obtain a reverse-ordered
+     * <i>view</i> of a sequenced map, use the {@link SequencedMap#reversed
+     * SequencedMap.reversed} method.
+     *
      * @param <T> 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 class Collections {
      * @since 1.6
      */
     public static <E> Set<E> newSetFromMap(Map<E, Boolean> map) {
+        if (! map.isEmpty()) // implicit null check
+            throw new IllegalArgumentException("Map is non-empty");
         return new SetFromMap<>(map);
     }
 
@@ -5692,12 +5913,10 @@ public class Collections {
         implements Set<E>, Serializable
     {
         @SuppressWarnings("serial") // Conditionally serializable
-        private final Map<E, Boolean> m;  // The backing map
+        final Map<E, Boolean> m;          // The backing map
         private transient Set<E> s;       // Its keySet
 
         SetFromMap(Map<E, Boolean> map) {
-            if (!map.isEmpty())
-                throw new IllegalArgumentException("Map is non-empty");
             m = map;
             s = map.keySet();
         }
@@ -5746,6 +5965,91 @@ public class Collections {
             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.
+     *
+     * <p>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.
+     *
+     * <p>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<String> set = Collections.newSequencedSetFromMap(
+     *         new LinkedHashMap<String, Boolean>() {
+     *             protected boolean removeEldestEntry(Map.Entry<String, Boolean> e) {
+     *                 return this.size() > 5;
+     *             }
+     *        });
+     * }
+     *
+     * @param <E> 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 <E> SequencedSet<E> newSequencedSetFromMap(SequencedMap<E, Boolean> map) {
+        if (! map.isEmpty()) // implicit null check
+            throw new IllegalArgumentException("Map is non-empty");
+        return new SequencedSetFromMap<>(map);
+    }
+
+    /**
+     * @serial include
+     */
+    private static class SequencedSetFromMap<E> extends SetFromMap<E> implements SequencedSet<E> {
+        private E nsee(Map.Entry<E, Boolean> e) {
+            if (e == null) {
+                throw new NoSuchElementException();
+            } else {
+                return e.getKey();
+            }
+        }
+
+        private SequencedMap<E, Boolean> map() {
+            return (SequencedMap<E, Boolean>) super.m;
+        }
+
+        SequencedSetFromMap(SequencedMap<E, Boolean> 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<E> 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 @@ public class Collections {
      * 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  <T> 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 90d7292c31a..13a2e150d97 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 @@ package java.util;
  * @since  1.6
  * @param <E> the type of elements held in this deque
  */
-public interface Deque<E> extends Queue<E> {
+public interface Deque<E> extends Queue<E>, SequencedCollection<E> {
     /**
      * 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<E> extends Queue<E> {
      */
     Iterator<E> 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<E> 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 9d4cdb346f2..e59bf7b089a 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<E>
     @java.io.Serial
     static final long serialVersionUID = -5024744406713321676L;
 
-    private transient HashMap<E,Object> map;
+    transient HashMap<E,Object> 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 3de7e1d5eae..398ebb14a54 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 @@ class ImmutableCollections {
             return indexOf(o) >= 0;
         }
 
+        @Override
+        public List<E> 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 1984043f0d0..d4052f396bf 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.Consumer;
 import java.util.function.BiConsumer;
 import java.util.function.BiFunction;
 import java.io.IOException;
+import java.util.function.Function;
 
 /**
  * <p>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
- * (<i>insertion-order</i>).  Note that insertion order is not affected
- * if a key is <i>re-inserted</i> into the map.  (A key {@code k} is
- * reinserted into a map {@code m} if {@code m.put(k, v)} is invoked when
+ * (<i>insertion-order</i>). 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 <i>re-inserted</i> 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.
  *
  * <p>This implementation spares its clients from the unspecified, generally
  * chaotic ordering provided by {@link HashMap} (and {@link Hashtable}),
@@ -59,7 +64,7 @@ import java.io.IOException;
  * order they were presented.)
  *
  * <p>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 (<i>access-order</i>).  This kind of map is well-suited to
  * building LRU caches.  Invoking the {@code put}, {@code putIfAbsent},
@@ -70,16 +75,24 @@ import java.io.IOException;
  * 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.
- * <i>No other methods generate entry accesses.</i>  In particular, operations
- * on collection-views do <i>not</i> affect the order of iteration of the
- * backing map.
+ * <i>No other methods generate entry accesses.</i> 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 <i>not</i> affect
+ * the encounter order of the backing map.
  *
  * <p>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.
  *
- * <p>This class provides all of the optional {@code Map} operations, and
- * permits null elements.  Like {@code HashMap}, it provides constant-time
+ * <p>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 @@ import java.io.IOException;
  */
 public class LinkedHashMap<K,V>
     extends HashMap<K,V>
-    implements Map<K,V>
+    implements SequencedMap<K,V>
 {
 
     /*
@@ -220,14 +233,25 @@ public class LinkedHashMap<K,V>
     // internal utilities
 
     // link at the end of list
-    private void linkNodeLast(LinkedHashMap.Entry<K,V> p) {
-        LinkedHashMap.Entry<K,V> last = tail;
-        tail = p;
-        if (last == null)
+    private void linkNodeAtEnd(LinkedHashMap.Entry<K,V> p) {
+        if (putMode == PUT_FIRST) {
+            LinkedHashMap.Entry<K,V> 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<K,V> last = tail;
+            tail = p;
+            if (last == null)
+                head = p;
+            else {
+                p.before = last;
+                last.after = p;
+            }
         }
     }
 
@@ -256,7 +280,7 @@ public class LinkedHashMap<K,V>
     Node<K,V> newNode(int hash, K key, V value, Node<K,V> e) {
         LinkedHashMap.Entry<K,V> p =
             new LinkedHashMap.Entry<>(hash, key, value, e);
-        linkNodeLast(p);
+        linkNodeAtEnd(p);
         return p;
     }
 
@@ -270,7 +294,7 @@ public class LinkedHashMap<K,V>
 
     TreeNode<K,V> newTreeNode(int hash, K key, V value, Node<K,V> next) {
         TreeNode<K,V> p = new TreeNode<>(hash, key, value, next);
-        linkNodeLast(p);
+        linkNodeAtEnd(p);
         return p;
     }
 
@@ -303,9 +327,17 @@ public class LinkedHashMap<K,V>
         }
     }
 
-    void afterNodeAccess(Node<K,V> 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<K,V> e) {
         LinkedHashMap.Entry<K,V> last;
-        if (accessOrder && (last = tail) != e) {
+        LinkedHashMap.Entry<K,V> first;
+        if ((putMode == PUT_LAST || (putMode == PUT_NORM && accessOrder)) && (last = tail) != e) {
+            // move node to last
             LinkedHashMap.Entry<K,V> p =
                 (LinkedHashMap.Entry<K,V>)e, b = p.before, a = p.after;
             p.after = null;
@@ -325,6 +357,61 @@ public class LinkedHashMap<K,V>
             }
             tail = p;
             ++modCount;
+        } else if (putMode == PUT_FIRST && (first = head) != e) {
+            // move node to first
+            LinkedHashMap.Entry<K,V> p =
+                (LinkedHashMap.Entry<K,V>)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}
+     * <p>
+     * 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}
+     * <p>
+     * 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 @@ public class LinkedHashMap<K,V>
     }
 
     /**
-     * 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 @@ public class LinkedHashMap<K,V>
      * @return a set view of the keys contained in this map
      */
     public Set<K> keySet() {
+        return sequencedKeySet();
+    }
+
+    /**
+     * {@inheritDoc}
+     * <p>
+     * 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<K> sequencedKeySet() {
         Set<K> ks = keySet;
         if (ks == null) {
-            ks = new LinkedKeySet();
-            keySet = ks;
+            SequencedSet<K> 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<K>) ks;
         }
-        return ks;
     }
 
-    @Override
+    static <K1,V1> Node<K1,V1> nsee(Node<K1,V1> node) {
+        if (node == null)
+            throw new NoSuchElementException();
+        else
+            return node;
+    }
+
     final <T> T[] keysToArray(T[] a) {
+        return keysToArray(a, false);
+    }
+
+    final <T> T[] keysToArray(T[] a, boolean reversed) {
         Object[] r = a;
         int idx = 0;
-        for (LinkedHashMap.Entry<K,V> e = head; e != null; e = e.after) {
-            r[idx++] = e.key;
+        if (reversed) {
+            for (LinkedHashMap.Entry<K,V> e = tail; e != null; e = e.before) {
+                r[idx++] = e.key;
+            }
+        } else {
+            for (LinkedHashMap.Entry<K,V> e = head; e != null; e = e.after) {
+                r[idx++] = e.key;
+            }
         }
         return a;
     }
 
-    @Override
-    final <T> T[] valuesToArray(T[] a) {
+    final <T> T[] valuesToArray(T[] a, boolean reversed) {
         Object[] r = a;
         int idx = 0;
-        for (LinkedHashMap.Entry<K,V> e = head; e != null; e = e.after) {
-            r[idx++] = e.value;
+        if (reversed) {
+            for (LinkedHashMap.Entry<K,V> e = tail; e != null; e = e.before) {
+                r[idx++] = e.value;
+            }
+        } else {
+            for (LinkedHashMap.Entry<K,V> e = head; e != null; e = e.after) {
+                r[idx++] = e.value;
+            }
         }
         return a;
     }
 
-    final class LinkedKeySet extends AbstractSet<K> {
+    final class LinkedKeySet extends AbstractSet<K> implements SequencedSet<K> {
+        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<K> 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 class LinkedHashMap<K,V>
         }
 
         public Object[] toArray() {
-            return keysToArray(new Object[size]);
+            return keysToArray(new Object[size], reversed);
         }
 
         public <T> 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<K,V> e = head; e != null; e = e.after)
-                action.accept(e.key);
+            if (reversed) {
+                for (LinkedHashMap.Entry<K,V> e = tail; e != null; e = e.before)
+                    action.accept(e.key);
+            } else {
+                for (LinkedHashMap.Entry<K,V> 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<K> 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 class LinkedHashMap<K,V>
      * @return a view of the values contained in this map
      */
     public Collection<V> values() {
-        Collection<V> vs = values;
-        if (vs == null) {
-            vs = new LinkedValues();
-            values = vs;
-        }
-        return vs;
+        return sequencedValues();
     }
 
-    final class LinkedValues extends AbstractCollection<V> {
+    /**
+     * {@inheritDoc}
+     * <p>
+     * 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<V> sequencedValues() {
+        Collection<V> vs = values;
+        if (vs == null) {
+            SequencedCollection<V> 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<V>) vs;
+        }
+    }
+
+    final class LinkedValues extends AbstractCollection<V> implements SequencedCollection<V> {
+        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<V> iterator() {
-            return new LinkedValueIterator();
+            return new LinkedValueIterator(reversed);
         }
         public final boolean contains(Object o) { return containsValue(o); }
         public final Spliterator<V> spliterator() {
@@ -640,26 +814,53 @@ public class LinkedHashMap<K,V>
         }
 
         public Object[] toArray() {
-            return valuesToArray(new Object[size]);
+            return valuesToArray(new Object[size], reversed);
         }
 
         public <T> 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<K,V> e = head; e != null; e = e.after)
-                action.accept(e.value);
+            if (reversed) {
+                for (LinkedHashMap.Entry<K,V> e = tail; e != null; e = e.before)
+                    action.accept(e.value);
+            } else {
+                for (LinkedHashMap.Entry<K,V> 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<V> 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 class LinkedHashMap<K,V>
      * @return a set view of the mappings contained in this map
      */
     public Set<Map.Entry<K,V>> entrySet() {
-        Set<Map.Entry<K,V>> es;
-        return (es = entrySet) == null ? (entrySet = new LinkedEntrySet()) : es;
+        return sequencedEntrySet();
     }
 
-    final class LinkedEntrySet extends AbstractSet<Map.Entry<K,V>> {
+    /**
+     * {@inheritDoc}
+     * <p>
+     * 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<Map.Entry<K, V>> sequencedEntrySet() {
+        Set<Map.Entry<K, V>> es = entrySet;
+        if (es == null) {
+            SequencedSet<Map.Entry<K, V>> 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<Map.Entry<K, V>>) es;
+        }
+    }
+
+    final class LinkedEntrySet extends AbstractSet<Map.Entry<K,V>>
+        implements SequencedSet<Map.Entry<K,V>> {
+        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<Map.Entry<K,V>> 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 class LinkedHashMap<K,V>
             if (action == null)
                 throw new NullPointerException();
             int mc = modCount;
-            for (LinkedHashMap.Entry<K,V> e = head; e != null; e = e.after)
-                action.accept(e);
+            if (reversed) {
+                for (LinkedHashMap.Entry<K,V> e = tail; e != null; e = e.before)
+                    action.accept(e);
+            } else {
+                for (LinkedHashMap.Entry<K,V> e = head; e != null; e = e.after)
+                    action.accept(e);
+            }
             if (modCount != mc)
                 throw new ConcurrentModificationException();
         }
+        final Node<K,V> nsee(Node<K,V> e) {
+            if (e == null)
+                throw new NoSuchElementException();
+            else
+                return e;
+        }
+        public final void addFirst(Map.Entry<K,V> e) { throw new UnsupportedOperationException(); }
+        public final void addLast(Map.Entry<K,V> e) { throw new UnsupportedOperationException(); }
+        public final Map.Entry<K,V> getFirst() { return nsee(reversed ? tail : head); }
+        public final Map.Entry<K,V> getLast() { return nsee(reversed ? head : tail); }
+        public final Map.Entry<K,V> removeFirst() {
+            var node = nsee(reversed ? tail : head);
+            removeNode(node.hash, node.key, null, false, false);
+            return node;
+        }
+        public final Map.Entry<K,V> removeLast() {
+            var node = nsee(reversed ? head : tail);
+            removeNode(node.hash, node.key, null, false, false);
+            return node;
+        }
+        public SequencedSet<Map.Entry<K,V>> reversed() {
+            if (reversed) {
+                return LinkedHashMap.this.sequencedEntrySet();
+            } else {
+                return new LinkedEntrySet(true);
+            }
+        }
     }
 
     // Map overrides
@@ -747,9 +1004,11 @@ public class LinkedHashMap<K,V>
         LinkedHashMap.Entry<K,V> next;
         LinkedHashMap.Entry<K,V> 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 @@ public class LinkedHashMap<K,V>
             if (e == null)
                 throw new NoSuchElementException();
             current = e;
-            next = e.after;
+            next = reversed ? e.before : e.after;
             return e;
         }
 
@@ -783,16 +1042,19 @@ public class LinkedHashMap<K,V>
 
     final class LinkedKeyIterator extends LinkedHashIterator
         implements Iterator<K> {
+        LinkedKeyIterator(boolean reversed) { super(reversed); }
         public final K next() { return nextNode().getKey(); }
     }
 
     final class LinkedValueIterator extends LinkedHashIterator
         implements Iterator<V> {
+        LinkedValueIterator(boolean reversed) { super(reversed); }
         public final V next() { return nextNode().value; }
     }
 
     final class LinkedEntryIterator extends LinkedHashIterator
         implements Iterator<Map.Entry<K,V>> {
+        LinkedEntryIterator(boolean reversed) { super(reversed); }
         public final Map.Entry<K,V> next() { return nextNode(); }
     }
 
@@ -816,4 +1078,175 @@ public class LinkedHashMap<K,V>
         return new LinkedHashMap<>(HashMap.calculateHashMapCapacity(numMappings));
     }
 
+    // Reversed View
+
+    /**
+     * {@inheritDoc}
+     * <p>
+     * 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<K, V> reversed() {
+        return new ReversedLinkedHashMapView<>(this);
+    }
+
+    static class ReversedLinkedHashMapView<K, V> extends AbstractMap<K, V>
+                                                 implements SequencedMap<K, V> {
+        final LinkedHashMap<K, V> base;
+
+        ReversedLinkedHashMapView(LinkedHashMap<K, V> 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<K> keySet() {
+            return base.sequencedKeySet().reversed();
+        }
+
+        public Collection<V> values() {
+            return base.sequencedValues().reversed();
+        }
+
+        public Set<Entry<K, V>> 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<K,V> 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<K,V> 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<K, V> reversed() {
+            return base;
+        }
+
+        public Entry<K, V> firstEntry() {
+            return base.lastEntry();
+        }
+
+        public Entry<K, V> lastEntry() {
+            return base.firstEntry();
+        }
+
+        public Entry<K, V> pollFirstEntry() {
+            return base.pollLastEntry();
+        }
+
+        public Entry<K, V> 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 330e37f8be3..fd9b52e2589 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 @@ package java.util;
 
 /**
  * <p>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
- * (<i>insertion-order</i>).  Note that insertion order is <i>not</i> affected
- * if an element is <i>re-inserted</i> 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
+ * (<i>insertion-order</i>). The least recently inserted element (the eldest) is
+ * first, and the youngest element is last. Note that encounter order is <i>not</i> affected
+ * if an element is <i>re-inserted</i> 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.
  *
  * <p>This implementation spares its clients from the unspecified, generally
  * chaotic ordering provided by {@link HashSet}, without incurring the
@@ -53,8 +57,8 @@ package java.util;
  * the copy.  (Clients generally appreciate having things returned in the same
  * order they were presented.)
  *
- * <p>This class provides all of the optional {@code Set} operations, and
- * permits null elements.  Like {@code HashSet}, it provides constant-time
+ * <p>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 @@ package java.util;
 
 public class LinkedHashSet<E>
     extends HashSet<E>
-    implements Set<E>, Cloneable, java.io.Serializable {
+    implements SequencedSet<E>, Cloneable, java.io.Serializable {
 
     @java.io.Serial
     private static final long serialVersionUID = -2851667679971038690L;
@@ -221,4 +225,100 @@ public class LinkedHashSet<E>
         return new LinkedHashSet<>(HashMap.calculateHashMapCapacity(numElements));
     }
 
+    @SuppressWarnings("unchecked")
+    LinkedHashMap<E, Object> map() {
+        return (LinkedHashMap<E, Object>) map;
+    }
+
+    /**
+     * {@inheritDoc}
+     * <p>
+     * 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}
+     * <p>
+     * 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}
+     * <p>
+     * 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<E> reversed() {
+        class ReverseLinkedHashSetView extends AbstractSet<E> implements SequencedSet<E> {
+            public int size()                  { return LinkedHashSet.this.size(); }
+            public Iterator<E> 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<E> reversed()  { return LinkedHashSet.this; }
+            public Object[] toArray() { return map().keysToArray(new Object[map.size()], true); }
+            public <T> 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 a504b4e8390..d7c67c98628 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 class LinkedList<E>
         }
     }
 
+    /**
+     * {@inheritDoc}
+     * <p>
+     * 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<E> 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<E> extends LinkedList<E> implements java.io.Externalizable {
+        final LinkedList<E> list;
+        final List<E> rlist;
+        final Deque<E> rdeque;
+
+        ReverseOrderLinkedListView(LinkedList<E> list, List<E> rlist, Deque<E> rdeque) {
+            this.list = list;
+            this.rlist = rlist;
+            this.rdeque = rdeque;
+        }
+
+        public String toString() {
+            return rlist.toString();
+        }
+
+        public boolean retainAll(Collection<?> c) {
+            return rlist.retainAll(c);
+        }
+
+        public boolean removeAll(Collection<?> c) {
+            return rlist.removeAll(c);
+        }
+
+        public boolean containsAll(Collection<?> c) {
+            return rlist.containsAll(c);
+        }
+
+        public boolean isEmpty() {
+            return rlist.isEmpty();
+        }
+
+        public Stream<E> parallelStream() {
+            return rlist.parallelStream();
+        }
+
+        public Stream<E> stream() {
+            return rlist.stream();
+        }
+
+        public boolean removeIf(Predicate<? super E> filter) {
+            return rlist.removeIf(filter);
+        }
+
+        public <T> T[] toArray(IntFunction<T[]> generator) {
+            return rlist.toArray(generator);
+        }
+
+        public void forEach(Consumer<? super E> action) {
+            rlist.forEach(action);
+        }
+
+        public Iterator<E> iterator() {
+            return rlist.iterator();
+        }
+
+        public int hashCode() {
+            return rlist.hashCode();
+        }
+
+        public boolean equals(Object o) {
+            return rlist.equals(o);
+        }
+
+        public List<E> subList(int fromIndex, int toIndex) {
+            return rlist.subList(fromIndex, toIndex);
+        }
+
+        public ListIterator<E> listIterator() {
+            return rlist.listIterator();
+        }
+
+        public void sort(Comparator<? super E> c) {
+            rlist.sort(c);
+        }
+
+        public void replaceAll(UnaryOperator<E> operator) {
+            rlist.replaceAll(operator);
+        }
+
+        public LinkedList<E> reversed() {
+            return list;
+        }
+
+        public Spliterator<E> spliterator() {
+            return rlist.spliterator();
+        }
+
+        public <T> T[] toArray(T[] a) {
+            return rlist.toArray(a);
+        }
+
+        public Object[] toArray() {
+            return rlist.toArray();
+        }
+
+        public Iterator<E> descendingIterator() {
+            return rdeque.descendingIterator();
+        }
+
+        public ListIterator<E> listIterator(int index) {
+            return rlist.listIterator(index);
+        }
+
+        public boolean removeLastOccurrence(Object o) {
+            return rdeque.removeLastOccurrence(o);
+        }
+
+        public boolean removeFirstOccurrence(Object o) {
+            return rdeque.removeFirstOccurrence(o);
+        }
+
+        public E pop() {
+            return rdeque.pop();
+        }
+
+        public void push(E e) {
+            rdeque.push(e);
+        }
+
+        public E pollLast() {
+            return rdeque.pollLast();
+        }
+
+        public E pollFirst() {
+            return rdeque.pollFirst();
+        }
+
+        public E peekLast() {
+            return rdeque.peekLast();
+        }
+
+        public E peekFirst() {
+            return rdeque.peekFirst();
+        }
+
+        public boolean offerLast(E e) {
+            return rdeque.offerLast(e);
+        }
+
+        public boolean offerFirst(E e) {
+            return rdeque.offerFirst(e);
+        }
+
+        public boolean offer(E e) {
+            return rdeque.offer(e);
+        }
+
+        public E remove() {
+            return rdeque.remove();
+        }
+
+        public E poll() {
+            return rdeque.poll();
+        }
+
+        public E element() {
+            return rdeque.element();
+        }
+
+        public E peek() {
+            return rdeque.peek();
+        }
+
+        public int lastIndexOf(Object o) {
+            return rlist.lastIndexOf(o);
+        }
+
+        public int indexOf(Object o) {
+            return rlist.indexOf(o);
+        }
+
+        public E remove(int index) {
+            return rlist.remove(index);
+        }
+
+        public void add(int index, E element) {
+            rlist.add(index, element);
+        }
+
+        public E set(int index, E element) {
+            return rlist.set(index, element);
+        }
+
+        public E get(int index) {
+            return rlist.get(index);
+        }
+
+        public void clear() {
+            rlist.clear();
+        }
+
+        public boolean addAll(int index, Collection<? extends E> c) {
+            return rlist.addAll(index, c);
+        }
+
+        public boolean addAll(Collection<? extends E> c) {
+            return rlist.addAll(c);
+        }
+
+        public boolean remove(Object o) {
+            return rlist.remove(o);
+        }
+
+        public boolean add(E e) {
+            return rlist.add(e);
+        }
+
+        public int size() {
+            return rlist.size();
+        }
+
+        public boolean contains(Object o) {
+            return rlist.contains(o);
+        }
+
+        public void addLast(E e) {
+            rdeque.addLast(e);
+        }
+
+        public void addFirst(E e) {
+            rdeque.addFirst(e);
+        }
+
+        public E removeLast() {
+            return rdeque.removeLast();
+        }
+
+        public E removeFirst() {
+            return rdeque.removeFirst();
+        }
+
+        public E getLast() {
+            return rdeque.getLast();
+        }
+
+        public E getFirst() {
+            return rdeque.getFirst();
+        }
+
+        public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
+            throw new java.io.InvalidObjectException("not serializable");
+        }
+
+        public void writeExternal(ObjectOutput out) throws IOException {
+            throw new java.io.InvalidObjectException("not serializable");
+        }
+    }
 }
diff --git a/src/java.base/share/classes/java/util/List.java b/src/java.base/share/classes/java/util/List.java
index 285aa8b536d..e9576455ca7 100644
--- a/src/java.base/share/classes/java/util/List.java
+++ b/src/java.base/share/classes/java/util/List.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1997, 2020, 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
@@ -28,10 +28,9 @@ package java.util;
 import java.util.function.UnaryOperator;
 
 /**
- * An ordered collection (also known as a <i>sequence</i>).  The user of this
- * interface has precise control over where in the list each element is
- * inserted.  The user can access elements by their integer index (position in
- * the list), and search for elements in the list.<p>
+ * An ordered collection, where the user has precise control over where in the
+ * list each element is inserted.  The user can access elements by their integer
+ * index (position in the list), and search for elements in the list.<p>
  *
  * Unlike sets, lists typically allow duplicate elements.  More formally,
  * lists typically allow pairs of elements {@code e1} and {@code e2}
@@ -139,7 +138,7 @@ import java.util.function.UnaryOperator;
  * @since 1.2
  */
 
-public interface List<E> extends Collection<E> {
+public interface List<E> extends SequencedCollection<E> {
     // Query Operations
 
     /**
@@ -781,6 +780,126 @@ public interface List<E> extends Collection<E> {
         }
     }
 
+    // ========== SequencedCollection ==========
+
+    /**
+     * {@inheritDoc}
+     *
+     * @implSpec
+     * The implementation in this interface calls {@code add(0, e)}.
+     *
+     * @throws NullPointerException {@inheritDoc}
+     * @throws UnsupportedOperationException {@inheritDoc}
+     * @since 21
+     */
+    default void addFirst(E e) {
+        this.add(0, e);
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * @implSpec
+     * The implementation in this interface calls {@code add(e)}.
+     *
+     * @throws NullPointerException {@inheritDoc}
+     * @throws UnsupportedOperationException {@inheritDoc}
+     * @since 21
+     */
+    default void addLast(E e) {
+        this.add(e);
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * @implSpec
+     * If this List is not empty, the implementation in this interface returns the result
+     * of calling {@code get(0)}. Otherwise, it throws {@code NoSuchElementException}.
+     *
+     * @throws NoSuchElementException {@inheritDoc}
+     * @since 21
+     */
+    default E getFirst() {
+        if (this.isEmpty()) {
+            throw new NoSuchElementException();
+        } else {
+            return this.get(0);
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * @implSpec
+     * If this List is not empty, the implementation in this interface returns the result
+     * of calling {@code get(size() - 1)}. Otherwise, it throws {@code NoSuchElementException}.
+     *
+     * @throws NoSuchElementException {@inheritDoc}
+     * @since 21
+     */
+    default E getLast() {
+        if (this.isEmpty()) {
+            throw new NoSuchElementException();
+        } else {
+            return this.get(this.size() - 1);
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * @implSpec
+     * If this List is not empty, the implementation in this interface returns the result
+     * of calling {@code remove(0)}. Otherwise, it throws {@code NoSuchElementException}.
+     *
+     * @throws NoSuchElementException {@inheritDoc}
+     * @throws UnsupportedOperationException {@inheritDoc}
+     * @since 21
+     */
+    default E removeFirst() {
+        if (this.isEmpty()) {
+            throw new NoSuchElementException();
+        } else {
+            return this.remove(0);
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * @implSpec
+     * If this List is not empty, the implementation in this interface returns the result
+     * of calling {@code remove(size() - 1)}. Otherwise, it throws {@code NoSuchElementException}.
+     *
+     * @throws NoSuchElementException {@inheritDoc}
+     * @throws UnsupportedOperationException {@inheritDoc}
+     * @since 21
+     */
+    default E removeLast() {
+        if (this.isEmpty()) {
+            throw new NoSuchElementException();
+        } else {
+            return this.remove(this.size() - 1);
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * @implSpec
+     * The implementation in this interface returns an instance of a reverse-ordered
+     * List that delegates its operations to this List.
+     *
+     * @return a reverse-ordered view of this collection, as a {@code List}
+     * @since 21
+     */
+    default List<E> reversed() {
+        return ReverseOrderListView.of(this, true); // we must assume it's modifiable
+    }
+
+    // ========== static methods ==========
+
     /**
      * Returns an unmodifiable list containing zero elements.
      *
diff --git a/src/java.base/share/classes/java/util/Map.java b/src/java.base/share/classes/java/util/Map.java
index 4e8a2496e2a..a36b8187106 100644
--- a/src/java.base/share/classes/java/util/Map.java
+++ b/src/java.base/share/classes/java/util/Map.java
@@ -42,8 +42,10 @@ import java.io.Serializable;
  * or set of key-value mappings.  The <i>order</i> of a map is defined as
  * the order in which the iterators on the map's collection views return their
  * elements.  Some map implementations, like the {@code TreeMap} class, make
- * specific guarantees as to their order; others, like the {@code HashMap}
- * class, do not.
+ * specific guarantees as to their encounter order; others, like the
+ * {@code HashMap} class, do not. Maps with a defined
+ * <a href="SequencedCollection.html#encounter">encounter order</a>
+ * are generally subtypes of the {@link SequencedMap} interface.
  *
  * <p>Note: great care must be exercised if mutable objects are used as map
  * keys.  The behavior of a map is not specified if the value of an object is
@@ -304,8 +306,10 @@ public interface Map<K, V> {
      * (optional operation).  The effect of this call is equivalent to that
      * of calling {@link #put(Object,Object) put(k, v)} on this map once
      * for each mapping from key {@code k} to value {@code v} in the
-     * specified map.  The behavior of this operation is undefined if the
-     * specified map is modified while the operation is in progress.
+     * specified map.  The behavior of this operation is undefined if the specified map
+     * is modified while the operation is in progress. If the specified map has a defined
+     * <a href="SequencedCollection.html#encounter">encounter order</a>,
+     * processing of its mappings generally occurs in that order.
      *
      * @param m mappings to be stored in this map
      * @throws UnsupportedOperationException if the {@code putAll} operation
diff --git a/src/java.base/share/classes/java/util/NavigableMap.java b/src/java.base/share/classes/java/util/NavigableMap.java
index 2e5b03b3474..92abc278dd8 100644
--- a/src/java.base/share/classes/java/util/NavigableMap.java
+++ b/src/java.base/share/classes/java/util/NavigableMap.java
@@ -429,4 +429,20 @@ public interface NavigableMap<K,V> extends SortedMap<K,V> {
      * @throws IllegalArgumentException {@inheritDoc}
      */
     SortedMap<K,V> tailMap(K fromKey);
+
+    /**
+     * {@inheritDoc}
+     * <p>
+     * This method is equivalent to {@link #descendingMap descendingMap}.
+     *
+     * @implSpec
+     * The implementation in this interface returns the result of calling the
+     * {@code descendingMap} method.
+     *
+     * @return a reverse-ordered view of this map, as a {@code NavigableMap}
+     * @since 21
+     */
+    default NavigableMap<K, V> reversed() {
+        return this.descendingMap();
+    }
 }
diff --git a/src/java.base/share/classes/java/util/NavigableSet.java b/src/java.base/share/classes/java/util/NavigableSet.java
index 49cd130243b..8d7da4eddd3 100644
--- a/src/java.base/share/classes/java/util/NavigableSet.java
+++ b/src/java.base/share/classes/java/util/NavigableSet.java
@@ -320,4 +320,58 @@ public interface NavigableSet<E> extends SortedSet<E> {
      * @throws IllegalArgumentException {@inheritDoc}
      */
     SortedSet<E> tailSet(E fromElement);
+
+    /**
+     * {@inheritDoc}
+     *
+     * @implSpec
+     * If this set is not empty, the implementation in this interface returns the result of calling
+     * the {@code pollFirst} method. Otherwise, it throws {@code NoSuchElementException}.
+     *
+     * @throws NoSuchElementException {@inheritDoc}
+     * @throws UnsupportedOperationException {@inheritDoc}
+     * @since 21
+     */
+    default E removeFirst() {
+        if (this.isEmpty()) {
+            throw new NoSuchElementException();
+        } else {
+            return this.pollFirst();
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * @implSpec
+     * If this set is not empty, the implementation in this interface returns the result of calling
+     * the {@code pollLast} method. Otherwise, it throws {@code NoSuchElementException}.
+     *
+     * @throws NoSuchElementException {@inheritDoc}
+     * @throws UnsupportedOperationException {@inheritDoc}
+     * @since 21
+     */
+    default E removeLast() {
+        if (this.isEmpty()) {
+            throw new NoSuchElementException();
+        } else {
+            return this.pollLast();
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     * <p>
+     * This method is equivalent to {@link #descendingSet descendingSet}.
+     *
+     * @implSpec
+     * The implementation in this interface returns the result of calling the
+     * {@code descendingSet} method.
+     *
+     * @return a reverse-ordered view of this collection, as a {@code NavigableSet}
+     * @since 21
+     */
+    default NavigableSet<E> reversed() {
+        return this.descendingSet();
+    }
 }
diff --git a/src/java.base/share/classes/java/util/ReverseOrderDequeView.java b/src/java.base/share/classes/java/util/ReverseOrderDequeView.java
new file mode 100644
index 00000000000..a580ad8f7db
--- /dev/null
+++ b/src/java.base/share/classes/java/util/ReverseOrderDequeView.java
@@ -0,0 +1,279 @@
+/*
+ * Copyright (c) 2021, 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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package java.util;
+
+import java.util.function.Consumer;
+import java.util.function.IntFunction;
+import java.util.stream.Stream;
+import java.util.stream.StreamSupport;
+import jdk.internal.util.ArraysSupport;
+
+/**
+ * Provides a reverse-ordered view of any Deque. Not serializable.
+ */
+class ReverseOrderDequeView<E> implements Deque<E> {
+    final Deque<E> base;
+
+    private ReverseOrderDequeView(Deque<E> deque) {
+        base = deque;
+    }
+
+    public static <T> Deque<T> of(Deque<T> deque) {
+        if (deque instanceof ReverseOrderDequeView<T> rodv) {
+            return rodv.base;
+        } else {
+            return new ReverseOrderDequeView<>(deque);
+        }
+    }
+
+    // ========== Iterable ==========
+
+    public void forEach(Consumer<? super E> action) {
+        for (E e : this)
+            action.accept(e);
+    }
+
+    public Iterator<E> iterator() {
+        return base.descendingIterator();
+    }
+
+    public Spliterator<E> spliterator() {
+        return Spliterators.spliteratorUnknownSize(base.descendingIterator(), 0);
+    }
+
+    // ========== Collection ==========
+
+    public boolean add(E e) {
+        base.addFirst(e);
+        return true;
+    }
+
+    public boolean addAll(Collection<? extends E> c) {
+        boolean modified = false;
+        for (E e : c) {
+            base.addFirst(e);
+            modified = true;
+        }
+        return modified;
+    }
+
+    public void clear() {
+        base.clear();
+    }
+
+    public boolean contains(Object o) {
+        return base.contains(o);
+    }
+
+    public boolean containsAll(Collection<?> c) {
+        return base.containsAll(c);
+    }
+
+    public boolean isEmpty() {
+        return base.isEmpty();
+    }
+
+    public Stream<E> parallelStream() {
+        return StreamSupport.stream(spliterator(), true);
+    }
+
+    // copied from AbstractCollection
+    public boolean remove(Object o) {
+        Iterator<E> it = iterator();
+        if (o==null) {
+            while (it.hasNext()) {
+                if (it.next()==null) {
+                    it.remove();
+                    return true;
+                }
+            }
+        } else {
+            while (it.hasNext()) {
+                if (o.equals(it.next())) {
+                    it.remove();
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    // copied from AbstractCollection
+    public boolean removeAll(Collection<?> c) {
+        Objects.requireNonNull(c);
+        boolean modified = false;
+        Iterator<?> it = iterator();
+        while (it.hasNext()) {
+            if (c.contains(it.next())) {
+                it.remove();
+                modified = true;
+            }
+        }
+        return modified;
+    }
+
+    // copied from AbstractCollection
+    public boolean retainAll(Collection<?> c) {
+        Objects.requireNonNull(c);
+        boolean modified = false;
+        Iterator<E> it = iterator();
+        while (it.hasNext()) {
+            if (!c.contains(it.next())) {
+                it.remove();
+                modified = true;
+            }
+        }
+        return modified;
+    }
+
+    public int size() {
+        return base.size();
+    }
+
+    public Stream<E> stream() {
+        return StreamSupport.stream(spliterator(), false);
+    }
+
+    public Object[] toArray() {
+        return ArraysSupport.reverse(base.toArray());
+    }
+
+    @SuppressWarnings("unchecked")
+    public <T> T[] toArray(T[] a) {
+        return ArraysSupport.toArrayReversed(base, a);
+    }
+
+    public <T> T[] toArray(IntFunction<T[]> generator) {
+        return ArraysSupport.reverse(base.toArray(generator));
+    }
+
+    // copied from AbstractCollection
+    public String toString() {
+        Iterator<E> it = iterator();
+        if (! it.hasNext())
+            return "[]";
+
+        StringBuilder sb = new StringBuilder();
+        sb.append('[');
+        for (;;) {
+            E e = it.next();
+            sb.append(e == this ? "(this Collection)" : e);
+            if (! it.hasNext())
+                return sb.append(']').toString();
+            sb.append(',').append(' ');
+        }
+    }
+
+    // ========== Deque and Queue ==========
+
+    public void addFirst(E e) {
+        base.addLast(e);
+    }
+
+    public void addLast(E e) {
+        base.addFirst(e);
+    }
+
+    public Iterator<E> descendingIterator() {
+        return base.iterator();
+    }
+
+    public E element() {
+        return base.getLast();
+    }
+
+    public E getFirst() {
+        return base.getLast();
+    }
+
+    public E getLast() {
+        return base.getFirst();
+    }
+
+    public boolean offer(E e) {
+        return base.offerFirst(e);
+    }
+
+    public boolean offerFirst(E e) {
+        return base.offerLast(e);
+    }
+
+    public boolean offerLast(E e) {
+        return base.offerFirst(e);
+    }
+
+    public E peek() {
+        return base.peekLast();
+    }
+
+    public E peekFirst() {
+        return base.peekLast();
+    }
+
+    public E peekLast() {
+        return base.peekFirst();
+    }
+
+    public E poll() {
+        return base.pollLast();
+    }
+
+    public E pollFirst() {
+        return base.pollLast();
+    }
+
+    public E pollLast() {
+        return base.pollFirst();
+    }
+
+    public E pop() {
+        return base.removeLast();
+    }
+
+    public void push(E e) {
+        base.addLast(e);
+    }
+
+    public E remove() {
+        return base.removeLast();
+    }
+
+    public E removeFirst() {
+        return base.removeLast();
+    }
+
+    public E removeLast() {
+        return base.removeFirst();
+    }
+
+    public boolean removeFirstOccurrence(Object o) {
+        return base.removeLastOccurrence(o);
+    }
+
+    public boolean removeLastOccurrence(Object o) {
+        return base.removeFirstOccurrence(o);
+    }
+}
diff --git a/src/java.base/share/classes/java/util/ReverseOrderListView.java b/src/java.base/share/classes/java/util/ReverseOrderListView.java
new file mode 100644
index 00000000000..0f7409bef16
--- /dev/null
+++ b/src/java.base/share/classes/java/util/ReverseOrderListView.java
@@ -0,0 +1,405 @@
+/*
+ * Copyright (c) 2021, 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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package java.util;
+
+import java.util.Objects;
+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;
+import java.util.stream.StreamSupport;
+import jdk.internal.util.ArraysSupport;
+
+/**
+ * Provides a reverse-ordered view of a List. Not serializable.
+ */
+class ReverseOrderListView<E> implements List<E> {
+
+    final List<E> base;
+    final boolean modifiable;
+
+    public static <T> List<T> of(List<T> list, boolean modifiable) {
+        if (list instanceof ReverseOrderListView<T> rolv) {
+            return rolv.base;
+        } else if (list instanceof RandomAccess) {
+            return new ReverseOrderListView.Rand<>(list, modifiable);
+        } else {
+            return new ReverseOrderListView<>(list, modifiable);
+        }
+    }
+
+    static class Rand<E> extends ReverseOrderListView<E> implements RandomAccess {
+        Rand(List<E> list, boolean modifiable) {
+            super(list, modifiable);
+        }
+    }
+
+    private ReverseOrderListView(List<E> list, boolean modifiable) {
+        this.base = list;
+        this.modifiable = modifiable;
+    }
+
+    /**
+     * Throws if this list is unmodifiable. This should be called from every mutator
+     * method. For bulk ops (addAll, removeAll, etc.) this throws unconditionally.
+     * In contrast, if the base list inherits a bulk op implementation from AbstractList,
+     * it might not throw if no actual mutation would be attempted (e.g., addAll on an
+     * empty collection). Arguably calling this is unnecessary for individual ops,
+     * for which the base list should always throw, but it's easier to verify the right
+     * behavior if every mutator of this class always checks.
+     */
+    void checkModifiable() {
+        if (! modifiable) {
+            throw new UnsupportedOperationException();
+        }
+    }
+
+    class DescendingIterator implements Iterator<E> {
+        final ListIterator<E> it = base.listIterator(base.size());
+        public boolean hasNext() { return it.hasPrevious(); }
+        public E next() { return it.previous(); }
+        public void remove() {
+            checkModifiable();
+            it.remove();
+            // TODO - make sure ListIterator is positioned correctly afterward
+        }
+    }
+
+    class DescendingListIterator implements ListIterator<E> {
+        final ListIterator<E> it;
+
+        DescendingListIterator(int size, int pos) {
+            if (pos < 0 || pos > size)
+                throw new IndexOutOfBoundsException();
+            it = base.listIterator(size - pos);
+        }
+
+        public boolean hasNext() {
+            return it.hasPrevious();
+        }
+
+        public E next() {
+            return it.previous();
+        }
+
+        public boolean hasPrevious() {
+            return it.hasNext();
+        }
+
+        public E previous() {
+            return it.next();
+        }
+
+        public int nextIndex() {
+            return base.size() - it.nextIndex();
+        }
+
+        public int previousIndex() {
+            return nextIndex() - 1;
+        }
+
+        public void remove() {
+            checkModifiable();
+            it.remove();
+        }
+
+        public void set(E e) {
+            checkModifiable();
+            it.set(e);
+        }
+
+        public void add(E e) {
+            checkModifiable();
+            it.add(e);
+            it.previous();
+        }
+    }
+
+    // ========== Iterable ==========
+
+    public void forEach(Consumer<? super E> action) {
+        for (E e : this)
+            action.accept(e);
+    }
+
+    public Iterator<E> iterator() {
+        return new DescendingIterator();
+    }
+
+    public Spliterator<E> spliterator() {
+        // TODO can probably improve this
+        return Spliterators.spliteratorUnknownSize(new DescendingIterator(), 0);
+    }
+
+    // ========== Collection ==========
+
+    public boolean add(E e) {
+        checkModifiable();
+        base.add(0, e);
+        return true;
+    }
+
+    public boolean addAll(Collection<? extends E> c) {
+        checkModifiable();
+
+        @SuppressWarnings("unchecked")
+        E[] adds = (E[]) c.toArray();
+        if (adds.length == 0) {
+            return false;
+        } else {
+            base.addAll(0, Arrays.asList(ArraysSupport.reverse(adds)));
+            return true;
+        }
+    }
+
+    public void clear() {
+        checkModifiable();
+        base.clear();
+    }
+
+    public boolean contains(Object o) {
+        return base.contains(o);
+    }
+
+    public boolean containsAll(Collection<?> c) {
+        return base.containsAll(c);
+    }
+
+    // copied from AbstractList
+    public boolean equals(Object o) {
+        if (o == this)
+            return true;
+        if (!(o instanceof List))
+            return false;
+
+        ListIterator<E> e1 = listIterator();
+        ListIterator<?> e2 = ((List<?>) o).listIterator();
+        while (e1.hasNext() && e2.hasNext()) {
+            E o1 = e1.next();
+            Object o2 = e2.next();
+            if (!(o1==null ? o2==null : o1.equals(o2)))
+                return false;
+        }
+        return !(e1.hasNext() || e2.hasNext());
+    }
+
+    // copied from AbstractList
+    public int hashCode() {
+        int hashCode = 1;
+        for (E e : this)
+            hashCode = 31*hashCode + (e==null ? 0 : e.hashCode());
+        return hashCode;
+    }
+
+    public boolean isEmpty() {
+        return base.isEmpty();
+    }
+
+    public Stream<E> parallelStream() {
+        return StreamSupport.stream(spliterator(), true);
+    }
+
+    // copied from AbstractCollection
+    public boolean remove(Object o) {
+        checkModifiable();
+        Iterator<E> it = iterator();
+        if (o==null) {
+            while (it.hasNext()) {
+                if (it.next()==null) {
+                    it.remove();
+                    return true;
+                }
+            }
+        } else {
+            while (it.hasNext()) {
+                if (o.equals(it.next())) {
+                    it.remove();
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    // copied from AbstractCollection
+    public boolean removeAll(Collection<?> c) {
+        checkModifiable();
+        Objects.requireNonNull(c);
+        boolean modified = false;
+        Iterator<?> it = iterator();
+        while (it.hasNext()) {
+            if (c.contains(it.next())) {
+                it.remove();
+                modified = true;
+            }
+        }
+        return modified;
+    }
+
+    // copied from AbstractCollection
+    public boolean retainAll(Collection<?> c) {
+        checkModifiable();
+        Objects.requireNonNull(c);
+        boolean modified = false;
+        Iterator<E> it = iterator();
+        while (it.hasNext()) {
+            if (!c.contains(it.next())) {
+                it.remove();
+                modified = true;
+            }
+        }
+        return modified;
+    }
+
+    public int size() {
+        return base.size();
+    }
+
+    public Stream<E> stream() {
+        return StreamSupport.stream(spliterator(), false);
+    }
+
+    public Object[] toArray() {
+        return ArraysSupport.reverse(base.toArray());
+    }
+
+    @SuppressWarnings("unchecked")
+    public <T> T[] toArray(T[] a) {
+        return ArraysSupport.toArrayReversed(base, a);
+    }
+
+    public <T> T[] toArray(IntFunction<T[]> generator) {
+        return ArraysSupport.reverse(base.toArray(generator));
+    }
+
+    // copied from AbstractCollection
+    public String toString() {
+        Iterator<E> it = iterator();
+        if (! it.hasNext())
+            return "[]";
+
+        StringBuilder sb = new StringBuilder();
+        sb.append('[');
+        for (;;) {
+            E e = it.next();
+            sb.append(e == this ? "(this Collection)" : e);
+            if (! it.hasNext())
+                return sb.append(']').toString();
+            sb.append(',').append(' ');
+        }
+    }
+
+    // ========== List ==========
+
+    public void add(int index, E element) {
+        checkModifiable();
+        int size = base.size();
+        checkClosedRange(index, size);
+        base.add(size - index, element);
+    }
+
+    public boolean addAll(int index, Collection<? extends E> c) {
+        checkModifiable();
+        int size = base.size();
+        checkClosedRange(index, size);
+        @SuppressWarnings("unchecked")
+        E[] adds = (E[]) c.toArray();
+        if (adds.length == 0) {
+            return false;
+        } else {
+            base.addAll(size - index, Arrays.asList(ArraysSupport.reverse(adds)));
+            return true;
+        }
+    }
+
+    public E get(int i) {
+        int size = base.size();
+        Objects.checkIndex(i, size);
+        return base.get(size - i - 1);
+    }
+
+    public int indexOf(Object o) {
+        int i = base.lastIndexOf(o);
+        return i == -1 ? -1 : base.size() - i - 1;
+    }
+
+    public int lastIndexOf(Object o) {
+        int i = base.indexOf(o);
+        return i == -1 ? -1 : base.size() - i - 1;
+    }
+
+    public ListIterator<E> listIterator() {
+        return new DescendingListIterator(base.size(), 0);
+    }
+
+    public ListIterator<E> listIterator(int index) {
+        int size = base.size();
+        checkClosedRange(index, size);
+        return new DescendingListIterator(size, index);
+    }
+
+    public E remove(int index) {
+        checkModifiable();
+        int size = base.size();
+        Objects.checkIndex(index, size);
+        return base.remove(size - index - 1);
+    }
+
+    public boolean removeIf(Predicate<? super E> filter) {
+        checkModifiable();
+        return base.removeIf(filter);
+    }
+
+    public void replaceAll(UnaryOperator<E> operator) {
+        checkModifiable();
+        base.replaceAll(operator);
+    }
+
+    public void sort(Comparator<? super E> c) {
+        checkModifiable();
+        base.sort(Collections.reverseOrder(c));
+    }
+
+    public E set(int index, E element) {
+        checkModifiable();
+        int size = base.size();
+        Objects.checkIndex(index, size);
+        return base.set(size - index - 1, element);
+    }
+
+    public List<E> subList(int fromIndex, int toIndex) {
+        int size = base.size();
+        Objects.checkFromToIndex(fromIndex, toIndex, size);
+        return new ReverseOrderListView<>(base.subList(size - toIndex, size - fromIndex), modifiable);
+    }
+
+    static void checkClosedRange(int index, int size) {
+        if (index < 0 || index > size) {
+            throw new IndexOutOfBoundsException("Index: " + index + ", Size: " + size);
+        }
+    }
+}
diff --git a/src/java.base/share/classes/java/util/ReverseOrderSortedMapView.java b/src/java.base/share/classes/java/util/ReverseOrderSortedMapView.java
new file mode 100644
index 00000000000..404950e8fd5
--- /dev/null
+++ b/src/java.base/share/classes/java/util/ReverseOrderSortedMapView.java
@@ -0,0 +1,473 @@
+/*
+ * Copyright (c) 2021, 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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package java.util;
+
+/**
+ * Provides a reversed-ordered view of a SortedMap. Not serializable.
+ *
+ * TODO: copy in equals and hashCode from AbstractMap
+ */
+class ReverseOrderSortedMapView<K, V> extends AbstractMap<K, V> implements SortedMap<K, V> {
+    final SortedMap<K, V> base;
+    final Comparator<? super K> cmp;
+
+    private ReverseOrderSortedMapView(SortedMap<K, V> map) {
+        base = map;
+        cmp = Collections.reverseOrder(map.comparator());
+    }
+
+    public static <K, V> SortedMap<K, V> of(SortedMap<K, V> map) {
+        if (map instanceof ReverseOrderSortedMapView<K, V> rosmv) {
+            return rosmv.base;
+        } else {
+            return new ReverseOrderSortedMapView<>(map);
+        }
+    }
+
+    // ========== Object ==========
+
+    // equals: inherited from AbstractMap
+
+    // hashCode: inherited from AbstractMap
+
+    public String toString() {
+        return toString(this, descendingEntryIterator(base));
+    }
+
+    // ========== Map ==========
+
+    public void clear() {
+        base.clear();
+    }
+
+    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 boolean isEmpty() {
+        return base.isEmpty();
+    }
+
+    public V put(K key, V value) {
+        return base.put(key, value);
+    }
+
+    public void putAll(Map<? extends K, ? extends V> m) {
+        base.putAll(m);
+    }
+
+    public V remove(Object key) {
+        return base.remove(key);
+    }
+
+    public int size() {
+        return base.size();
+    }
+
+    public Set<K> keySet() {
+        return new AbstractSet<>() {
+            // inherit add(), which throws UOE
+            public Iterator<K> iterator() { return descendingKeyIterator(base); }
+            public int size() { return base.size(); }
+            public void clear() { base.keySet().clear(); }
+            public boolean contains(Object o) { return base.keySet().contains(o); }
+            public boolean remove(Object o) { return base.keySet().remove(o); }
+        };
+    }
+
+    public Collection<V> values() {
+        return new AbstractCollection<>() {
+            // inherit add(), which throws UOE
+            public Iterator<V> iterator() { return descendingValueIterator(base); }
+            public int size() { return base.size(); }
+            public void clear() { base.values().clear(); }
+            public boolean contains(Object o) { return base.values().contains(o); }
+            public boolean remove(Object o) { return base.values().remove(o); }
+        };
+    }
+
+    public Set<Entry<K, V>> entrySet() {
+        return new AbstractSet<>() {
+            // inherit add(), which throws UOE
+            public Iterator<Entry<K, V>> iterator() { return descendingEntryIterator(base); }
+            public int size() { return base.size(); }
+            public void clear() { base.entrySet().clear(); }
+            public boolean contains(Object o) { return base.entrySet().contains(o); }
+            public boolean remove(Object o) { return base.entrySet().remove(o); }
+        };
+    }
+
+    // ========== SequencedMap ==========
+
+    public SortedMap<K, V> reversed() {
+        return base;
+    }
+
+    public K firstKey() {
+        return base.lastKey();
+    }
+
+    public K lastKey() {
+        return base.firstKey();
+    }
+
+    public Map.Entry<K, V> firstEntry() {
+        return base.lastEntry();
+    }
+
+    public Map.Entry<K, V> lastEntry() {
+        return base.firstEntry();
+    }
+
+    public Map.Entry<K,V> pollFirstEntry() {
+        return base.pollLastEntry();
+    }
+
+    public Map.Entry<K,V> 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);
+    }
+
+    // ========== SortedMap ==========
+
+    public Comparator<? super K> comparator() {
+        return cmp;
+    }
+
+    public SortedMap<K, V> subMap(K fromKey, K toKey) {
+        if (cmp.compare(fromKey, toKey) <= 0) {
+            return new Submap(fromKey, toKey);
+        } else {
+            throw new IllegalArgumentException();
+        }
+    }
+
+    public SortedMap<K, V> headMap(K toKey) {
+        return new Submap(null, toKey);
+    }
+
+    public SortedMap<K, V> tailMap(K fromKey) {
+        return new Submap(fromKey, null);
+    }
+
+    // ========== Infrastructure ==========
+
+    static <K, V> Iterator<K> descendingKeyIterator(SortedMap<K, V> map) {
+        return new Iterator<>() {
+            SortedMap<K, V> root = map;
+            SortedMap<K, V> view = map;
+            K prev = null;
+
+            public boolean hasNext() {
+                return ! view.isEmpty();
+            }
+
+            public K next() {
+                if (view.isEmpty())
+                    throw new NoSuchElementException();
+                K k = prev = view.lastKey();
+                view = root.headMap(k);
+                return k;
+            }
+
+            public void remove() {
+                if (prev == null) {
+                    throw new IllegalStateException();
+                } else {
+                    root.remove(prev);
+                    prev = null;
+                }
+            }
+        };
+    }
+
+    static <K, V> Iterator<V> descendingValueIterator(SortedMap<K, V> map) {
+        return new Iterator<>() {
+            Iterator<K> keyIterator = descendingKeyIterator(map);
+
+            public boolean hasNext() {
+                return keyIterator.hasNext();
+            }
+
+            public V next() {
+                return map.get(keyIterator.next());
+            }
+
+            public void remove() {
+                keyIterator.remove();
+            }
+        };
+    }
+
+    static <K, V> Iterator<Map.Entry<K, V>> descendingEntryIterator(SortedMap<K, V> map) {
+        return new Iterator<>() {
+            Iterator<K> keyIterator = descendingKeyIterator(map);
+
+            public boolean hasNext() {
+                return keyIterator.hasNext();
+            }
+
+            public Map.Entry<K, V> next() {
+                K key = keyIterator.next();
+                return new ViewEntry<>(map, key, map.get(key));
+            }
+
+            public void remove() {
+                keyIterator.remove();
+            }
+        };
+    }
+
+    static class ViewEntry<K, V> implements Map.Entry<K, V> {
+        final Map<K, V> map;
+        final K key;
+        final V value;
+
+        ViewEntry(Map<K, V> map, K key, V value) {
+            this.map = map;
+            this.key = key;
+            this.value = value;
+        }
+
+        public K getKey()             { return key; }
+        public V getValue()           { return value; }
+        public V setValue(V newValue) { return map.put(key, newValue); }
+
+        public boolean equals(Object o) {
+            return o instanceof Map.Entry<?, ?> e
+                    && Objects.equals(key, e.getKey())
+                    && Objects.equals(value, e.getValue());
+        }
+
+        public int hashCode() {
+            return Objects.hashCode(key) ^ Objects.hashCode(value);
+        }
+
+        public String toString() {
+            return key + "=" + value;
+        }
+    }
+
+    // copied and modified from AbstractMap
+    static <K, V> String toString(Map<K, V> thisMap, Iterator<Entry<K,V>> i) {
+        if (! i.hasNext())
+            return "{}";
+
+        StringBuilder sb = new StringBuilder();
+        sb.append('{');
+        for (;;) {
+            Entry<K,V> e = i.next();
+            K key = e.getKey();
+            V value = e.getValue();
+            sb.append(key   == thisMap ? "(this Map)" : key);
+            sb.append('=');
+            sb.append(value == thisMap ? "(this Map)" : value);
+            if (! i.hasNext())
+                return sb.append('}').toString();
+            sb.append(',').append(' ');
+        }
+    }
+
+    /**
+     * Used for various submap views. We can't use the base SortedMap's subMap,
+     * because of the asymmetry between from-inclusive and to-exclusive.
+     */
+    class Submap extends AbstractMap<K, V> implements SortedMap<K, V> {
+        final K head; // head key, or negative infinity if null
+        final K tail; // tail key, or positive infinity if null
+
+        @SuppressWarnings("unchecked")
+        Submap(K head, K tail) {
+            this.head = head;
+            this.tail = tail;
+        }
+
+        // returns whether e is above the head, inclusive
+        boolean aboveHead(K k) {
+            return head == null || cmp.compare(k, head) >= 0;
+        }
+
+        // returns whether e is below the tail, exclusive
+        boolean belowTail(K k) {
+            return tail == null || cmp.compare(k, tail) < 0;
+        }
+
+        Iterator<Entry<K, V>> entryIterator() {
+            return new Iterator<>() {
+                Entry<K, V> cache = null;
+                K prevKey = null;
+                boolean dead = false;
+                Iterator<Entry<K, V>> it = descendingEntryIterator(base);
+
+                public boolean hasNext() {
+                    if (dead)
+                        return false;
+
+                    if (cache != null)
+                        return true;
+
+                    while (it.hasNext()) {
+                        Entry<K, V> e = it.next();
+
+                        if (! aboveHead(e.getKey()))
+                            continue;
+
+                        if (! belowTail(e.getKey())) {
+                            dead = true;
+                            return false;
+                        }
+
+                        cache = e;
+                        return true;
+                    }
+
+                    return false;
+                }
+
+                public Entry<K, V> next() {
+                    if (hasNext()) {
+                        Entry<K, V> e = cache;
+                        cache = null;
+                        prevKey = e.getKey();
+                        return e;
+                    } else {
+                        throw new NoSuchElementException();
+                    }
+                }
+
+                public void remove() {
+                    if (prevKey == null) {
+                        throw new IllegalStateException();
+                    } else {
+                        base.remove(prevKey);
+                    }
+                }
+            };
+        }
+
+        // equals: inherited from AbstractMap
+
+        // hashCode: inherited from AbstractMap
+
+        public String toString() {
+            return ReverseOrderSortedMapView.toString(this, entryIterator());
+        }
+
+        public Set<Entry<K, V>> entrySet() {
+            return new AbstractSet<>() {
+                public Iterator<Entry<K, V>> iterator() {
+                    return entryIterator();
+                }
+
+                public int size() {
+                    int sz = 0;
+                    for (var it = entryIterator(); it.hasNext();) {
+                        it.next();
+                        sz++;
+                    }
+                    return sz;
+                }
+            };
+        }
+
+        public V put(K key, V value) {
+            if (aboveHead(key) && belowTail(key))
+                return base.put(key, value);
+            else
+                throw new IllegalArgumentException();
+        }
+
+        public V remove(Object o) {
+            @SuppressWarnings("unchecked")
+            K key = (K) o;
+            if (aboveHead(key) && belowTail(key))
+                return base.remove(o);
+            else
+                return null;
+        }
+
+        public int size() {
+            return entrySet().size();
+        }
+
+        public Comparator<? super K> comparator() {
+            return cmp;
+        }
+
+        public K firstKey() {
+            return this.entryIterator().next().getKey();
+        }
+
+        public K lastKey() {
+            var it = this.entryIterator();
+            if (! it.hasNext())
+                throw new NoSuchElementException();
+            var last = it.next();
+            while (it.hasNext())
+                last = it.next();
+            return last.getKey();
+        }
+
+        public SortedMap<K, V> subMap(K from, K to) {
+            if (aboveHead(from) && belowTail(from) &&
+                aboveHead(to) && belowTail(to) &&
+                cmp.compare(from, to) <= 0) {
+                return new Submap(from, to);
+            } else {
+                throw new IllegalArgumentException();
+            }
+        }
+
+        public SortedMap<K, V> headMap(K to) {
+            if (aboveHead(to) && belowTail(to))
+                return new Submap(head, to);
+            else
+                throw new IllegalArgumentException();
+        }
+
+        public SortedMap<K, V> tailMap(K from) {
+            if (aboveHead(from) && belowTail(from))
+                return new Submap(from, tail);
+            else
+                throw new IllegalArgumentException();
+        }
+    }
+}
diff --git a/src/java.base/share/classes/java/util/ReverseOrderSortedSetView.java b/src/java.base/share/classes/java/util/ReverseOrderSortedSetView.java
new file mode 100644
index 00000000000..678c82fc311
--- /dev/null
+++ b/src/java.base/share/classes/java/util/ReverseOrderSortedSetView.java
@@ -0,0 +1,373 @@
+/*
+ * Copyright (c) 2021, 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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package java.util;
+
+import java.util.function.Consumer;
+import java.util.function.IntFunction;
+import java.util.stream.Stream;
+import java.util.stream.StreamSupport;
+import jdk.internal.util.ArraysSupport;
+
+/**
+ * Provides a reversed-ordered view of a SortedSet. Not serializable.
+ */
+class ReverseOrderSortedSetView<E> implements SortedSet<E> {
+    final SortedSet<E> base;
+    final Comparator<? super E> comp;
+
+    private ReverseOrderSortedSetView(SortedSet<E> set) {
+        base = set;
+        comp = Collections.reverseOrder(set.comparator());
+    }
+
+    public static <T> SortedSet<T> of(SortedSet<T> set) {
+        if (set instanceof ReverseOrderSortedSetView<T> rossv) {
+            return rossv.base;
+        } else {
+            return new ReverseOrderSortedSetView<>(set);
+        }
+    }
+
+    // ========== Object ==========
+
+    // copied from AbstractSet
+    public boolean equals(Object o) {
+        if (o == this)
+            return true;
+
+        if (!(o instanceof Set))
+            return false;
+        Collection<?> c = (Collection<?>) o;
+        if (c.size() != size())
+            return false;
+        try {
+            return containsAll(c);
+        } catch (ClassCastException | NullPointerException unused) {
+            return false;
+        }
+    }
+
+    // copied from AbstractSet
+    public int hashCode() {
+        int h = 0;
+        Iterator<E> i = iterator();
+        while (i.hasNext()) {
+            E obj = i.next();
+            if (obj != null)
+                h += obj.hashCode();
+        }
+        return h;
+    }
+
+    // copied from AbstractCollection
+    public String toString() {
+        Iterator<E> it = iterator();
+        if (! it.hasNext())
+            return "[]";
+
+        StringBuilder sb = new StringBuilder();
+        sb.append('[');
+        for (;;) {
+            E e = it.next();
+            sb.append(e == this ? "(this Collection)" : e);
+            if (! it.hasNext())
+                return sb.append(']').toString();
+            sb.append(',').append(' ');
+        }
+    }
+
+    // ========== Iterable ==========
+
+    public void forEach(Consumer<? super E> action) {
+        for (E e : this)
+            action.accept(e);
+    }
+
+    public Iterator<E> iterator() {
+        return descendingIterator(base);
+    }
+
+    public Spliterator<E> spliterator() {
+        return Spliterators.spliteratorUnknownSize(descendingIterator(base), 0);
+    }
+
+    // ========== Collection ==========
+
+    public boolean add(E e) {
+        base.add(e);
+        return true;
+    }
+
+    public boolean addAll(Collection<? extends E> c) {
+        return base.addAll(c);
+    }
+
+    public void clear() {
+        base.clear();
+    }
+
+    public boolean contains(Object o) {
+        return base.contains(o);
+    }
+
+    public boolean containsAll(Collection<?> c) {
+        return base.containsAll(c);
+    }
+
+    public boolean isEmpty() {
+        return base.isEmpty();
+    }
+
+    public Stream<E> parallelStream() {
+        return StreamSupport.stream(spliterator(), true);
+    }
+
+    public boolean remove(Object o) {
+        return base.remove(o);
+    }
+
+    public boolean removeAll(Collection<?> c) {
+        return base.removeAll(c);
+    }
+
+    // copied from AbstractCollection
+    public boolean retainAll(Collection<?> c) {
+        return base.retainAll(c);
+    }
+
+    public int size() {
+        return base.size();
+    }
+
+    public Stream<E> stream() {
+        return StreamSupport.stream(spliterator(), false);
+    }
+
+    public Object[] toArray() {
+        return ArraysSupport.reverse(base.toArray());
+    }
+
+    @SuppressWarnings("unchecked")
+    public <T> T[] toArray(T[] a) {
+        return ArraysSupport.toArrayReversed(base, a);
+    }
+
+    public <T> T[] toArray(IntFunction<T[]> generator) {
+        return ArraysSupport.reverse(base.toArray(generator));
+    }
+
+    // ========== SortedSet ==========
+
+    public Comparator<? super E> comparator() {
+        return comp;
+    }
+
+    public E first() { return base.last(); }
+
+    public E last() { return base.first(); }
+
+    public SortedSet<E> headSet(E to) {
+        return new Subset(null, to);
+    }
+
+    public SortedSet<E> subSet(E from, E to) {
+        return new Subset(from, to);
+    }
+
+    public SortedSet<E> tailSet(E from) {
+        return new Subset(from, null);
+    }
+
+    // ========== Infrastructure ==========
+
+    static <T> Iterator<T> descendingIterator(SortedSet<T> set) {
+        return new Iterator<>() {
+            SortedSet<T> root = set;
+            SortedSet<T> view = set;
+            T prev = null;
+
+            public boolean hasNext() {
+                return ! view.isEmpty();
+            }
+
+            public T next() {
+                if (view.isEmpty())
+                    throw new NoSuchElementException();
+                T t = prev = view.last();
+                view = root.headSet(t);
+                return t;
+            }
+
+            public void remove() {
+                if (prev == null) {
+                    throw new IllegalStateException();
+                } else {
+                    root.remove(prev);
+                    prev = null;
+                }
+            }
+        };
+    }
+
+    /**
+     * Used for various subset views. We can't use the base SortedSet's subset,
+     * because of the asymmetry between from-inclusive and to-exclusive.
+     */
+    class Subset extends AbstractSet<E> implements SortedSet<E> {
+        final E head; // head element, or negative infinity if null
+        final E tail; // tail element, or positive infinity if null
+        final Comparator<E> cmp;
+
+        @SuppressWarnings("unchecked")
+        Subset(E head, E tail) {
+            this.head = head;
+            this.tail = tail;
+            Comparator<E> c = (Comparator<E>) ReverseOrderSortedSetView.this.comparator();
+            if (c == null)
+                c = (Comparator<E>) Comparator.naturalOrder();
+            cmp = c;
+        }
+
+        // returns whether e is above the head, inclusive
+        boolean aboveHead(E e) {
+            return head == null || cmp.compare(e, head) >= 0;
+        }
+
+        // returns whether e is below the tail, exclusive
+        boolean belowTail(E e) {
+            return tail == null || cmp.compare(e, tail) < 0;
+        }
+
+        public Iterator<E> iterator() {
+            return new Iterator<>() {
+                E cache = null;
+                boolean dead = false;
+                Iterator<E> it = descendingIterator(base);
+
+                public boolean hasNext() {
+                    if (dead)
+                        return false;
+
+                    if (cache != null)
+                        return true;
+
+                    while (it.hasNext()) {
+                        E e = it.next();
+
+                        if (! aboveHead(e))
+                            continue;
+
+                        if (! belowTail(e)) {
+                            dead = true;
+                            return false;
+                        }
+
+                        cache = e;
+                        return true;
+                    }
+
+                    return false;
+                }
+
+                public E next() {
+                    if (hasNext()) {
+                        E e = cache;
+                        cache = null;
+                        return e;
+                    } else {
+                        throw new NoSuchElementException();
+                    }
+                }
+            };
+        }
+
+        public boolean add(E e) {
+            if (aboveHead(e) && belowTail(e))
+                return base.add(e);
+            else
+                throw new IllegalArgumentException();
+        }
+
+        public boolean remove(Object o) {
+            @SuppressWarnings("unchecked")
+            E e = (E) o;
+            if (aboveHead(e) && belowTail(e))
+                return base.remove(o);
+            else
+                return false;
+        }
+
+        public int size() {
+            int sz = 0;
+            for (E e : this)
+                sz++;
+            return sz;
+        }
+
+        public Comparator<? super E> comparator() {
+            return ReverseOrderSortedSetView.this.comparator();
+        }
+
+        public E first() {
+            return this.iterator().next();
+        }
+
+        public E last() {
+            var it = this.iterator();
+            if (! it.hasNext())
+                throw new NoSuchElementException();
+            E last = it.next();
+            while (it.hasNext())
+                last = it.next();
+            return last;
+        }
+
+        public SortedSet<E> subSet(E from, E to) {
+            if (aboveHead(from) && belowTail(from) &&
+                aboveHead(to) && belowTail(to) &&
+                cmp.compare(from, to) <= 0) {
+                return ReverseOrderSortedSetView.this.new Subset(from, to);
+            } else {
+                throw new IllegalArgumentException();
+            }
+        }
+
+        public SortedSet<E> headSet(E to) {
+            if (aboveHead(to) && belowTail(to))
+                return ReverseOrderSortedSetView.this.new Subset(head, to);
+            else
+                throw new IllegalArgumentException();
+        }
+
+        public SortedSet<E> tailSet(E from) {
+            if (aboveHead(from) && belowTail(from))
+                return ReverseOrderSortedSetView.this.new Subset(null, tail);
+            else
+                throw new IllegalArgumentException();
+        }
+
+    }
+}
diff --git a/src/java.base/share/classes/java/util/SequencedCollection.java b/src/java.base/share/classes/java/util/SequencedCollection.java
new file mode 100644
index 00000000000..54237c7a3f7
--- /dev/null
+++ b/src/java.base/share/classes/java/util/SequencedCollection.java
@@ -0,0 +1,203 @@
+/*
+ * Copyright (c) 2021, 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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package java.util;
+
+/**
+ * A collection that has a well-defined encounter order, that supports operations at both ends,
+ * and that is reversible. The elements of a sequenced collection have an <a id="encounter">
+ * <i>encounter order</i></a>, where conceptually the elements have a linear arrangement
+ * from the first element to the last element. Given any two elements, one element is
+ * either before (closer to the first element) or after (closer to the last element)
+ * the other element.
+ * <p>
+ * (Note that this definition does not imply anything about physical positioning
+ * of elements, such as their locations in a computer's memory.)
+ * <p>
+ * Several methods inherited from the {@link Collection} interface are required to operate
+ * on elements according to this collection's encounter order. For instance, the
+ * {@link Collection#iterator iterator} method provides elements starting from the first element,
+ * proceeding through successive elements, until the last element. Other methods that are
+ * required to operate on elements in encounter order include the following:
+ * {@link Iterable#forEach forEach}, {@link Collection#parallelStream parallelStream},
+ * {@link Collection#spliterator spliterator}, {@link Collection#stream stream},
+ * and all overloads of the {@link Collection#toArray toArray} method.
+ * <p>
+ * This interface provides methods to add, retrieve, and remove elements at either end
+ * of the collection.
+ * <p>
+ * This interface also defines the {@link #reversed reversed} method, which provides
+ * a reverse-ordered <a href="Collection.html#view">view</a> of this collection.
+ * In the reverse-ordered view, the concepts of first and last are inverted, as are
+ * the concepts of successor and predecessor. The first element of this collection is
+ * the last element of the reverse-ordered view, and vice-versa. The successor of some
+ * element in this collection is its predecessor in the reversed view, and vice-versa. All
+ * methods that respect the encounter order of the collection operate as if the encounter order
+ * is inverted. For instance, the {@link #iterator} method of the reversed view reports the
+ * elements in order from the last element of this collection to the first. The availability of
+ * the {@code reversed} method, and its impact on the ordering semantics of all applicable
+ * methods, allow convenient iteration, searching, copying, and streaming of the elements of
+ * this collection in either forward order or reverse order.
+ * <p>
+ * This class is a member of the
+ * <a href="{@docRoot}/java.base/java/util/package-summary.html#CollectionsFramework">
+ * Java Collections Framework</a>.
+ *
+ * @apiNote
+ * This interface does not impose any requirements on the {@code equals} and {@code hashCode}
+ * methods, because requirements imposed by sub-interfaces {@link List} and {@link SequencedSet}
+ * (which inherits requirements from {@link Set}) would be in conflict. See the specifications for
+ * {@link Collection#equals Collection.equals} and {@link Collection#hashCode Collection.hashCode}
+ * for further information.
+ *
+ * @param <E> the type of elements in this collection
+ * @since 21
+ */
+public interface SequencedCollection<E> extends Collection<E> {
+    /**
+     * Returns a reverse-ordered <a href="Collection.html#view">view</a> of this collection.
+     * The encounter order of elements in the returned view is the inverse of the encounter
+     * order of elements in this collection. The reverse ordering affects all order-sensitive
+     * operations, including those on the view collections of the returned view. If the collection
+     * implementation permits modifications to this view, the modifications "write through" to the
+     * underlying collection. Changes to the underlying collection might or might not be visible
+     * in this reversed view, depending upon the implementation.
+     *
+     * @return a reverse-ordered view of this collection
+     */
+    SequencedCollection<E> reversed();
+
+    /**
+     * Adds an element as the first element of this collection (optional operation).
+     * After this operation completes normally, the given element will be a member of
+     * this collection, and it will be the first element in encounter order.
+     *
+     * @implSpec
+     * The implementation in this interface always throws {@code UnsupportedOperationException}.
+     *
+     * @param e the element to be added
+     * @throws NullPointerException if the specified element is null and this
+     *         collection does not permit null elements
+     * @throws UnsupportedOperationException if this collection implementation
+     *         does not support this operation
+     */
+    default void addFirst(E e) {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * Adds an element as the last element of this collection (optional operation).
+     * After this operation completes normally, the given element will be a member of
+     * this collection, and it will be the last element in encounter order.
+     *
+     * @implSpec
+     * The implementation in this interface always throws {@code UnsupportedOperationException}.
+     *
+     * @param e the element to be added.
+     * @throws NullPointerException if the specified element is null and this
+     *         collection does not permit null elements
+     * @throws UnsupportedOperationException if this collection implementation
+     *         does not support this operation
+     */
+    default void addLast(E e) {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * Gets the first element of this collection.
+     *
+     * @implSpec
+     * The implementation in this interface obtains an iterator of this collection, and
+     * then it obtains an element by calling the iterator's {@code next} method. Any
+     * {@code NoSuchElementException} thrown is propagated. Otherwise, it returns
+     * the element.
+     *
+     * @return the retrieved element
+     * @throws NoSuchElementException if this collection is empty
+     */
+    default E getFirst() {
+        return this.iterator().next();
+    }
+
+    /**
+     * Gets the last element of this collection.
+     *
+     * @implSpec
+     * The implementation in this interface obtains an iterator of the reversed view
+     * of this collection, and then it obtains an element by calling the iterator's
+     * {@code next} method. Any {@code NoSuchElementException} thrown is propagated.
+     * Otherwise, it returns the element.
+     *
+     * @return the retrieved element
+     * @throws NoSuchElementException if this collection is empty
+     */
+    default E getLast() {
+        return this.reversed().iterator().next();
+    }
+
+    /**
+     * Removes and returns the first element of this collection (optional operation).
+     *
+     * @implSpec
+     * The implementation in this interface obtains an iterator of this collection, and then
+     * it obtains an element by calling the iterator's {@code next} method. Any
+     * {@code NoSuchElementException} thrown is propagated. It then calls the iterator's
+     * {@code remove} method. Any {@code UnsupportedOperationException} thrown is propagated.
+     * Then, it returns the element.
+     *
+     * @return the removed element
+     * @throws NoSuchElementException if this collection is empty
+     * @throws UnsupportedOperationException if this collection implementation
+     *         does not support this operation
+     */
+    default E removeFirst() {
+        var it = this.iterator();
+        E e = it.next();
+        it.remove();
+        return e;
+    }
+
+    /**
+     * Removes and returns the last element of this collection (optional operation).
+     *
+     * @implSpec
+     * The implementation in this interface obtains an iterator of the reversed view of this
+     * collection, and then it obtains an element by calling the iterator's {@code next} method.
+     * Any {@code NoSuchElementException} thrown is propagated. It then calls the iterator's
+     * {@code remove} method. Any {@code UnsupportedOperationException} thrown is propagated.
+     * Then, it returns the element.
+     *
+     * @return the removed element
+     * @throws NoSuchElementException if this collection is empty
+     * @throws UnsupportedOperationException if this collection implementation
+     *         does not support this operation
+     */
+    default E removeLast() {
+        var it = this.reversed().iterator();
+        E e = it.next();
+        it.remove();
+        return e;
+    }
+}
diff --git a/src/java.base/share/classes/java/util/SequencedMap.java b/src/java.base/share/classes/java/util/SequencedMap.java
new file mode 100644
index 00000000000..c8f88d004bc
--- /dev/null
+++ b/src/java.base/share/classes/java/util/SequencedMap.java
@@ -0,0 +1,331 @@
+/*
+ * Copyright (c) 2021, 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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package java.util;
+
+/**
+ * A Map that has a well-defined encounter order, that supports operations at both ends, and
+ * that is reversible. The <a href="SequencedCollection.html#encounter">encounter order</a>
+ * of a {@code SequencedMap} is similar to that of the elements of a {@link SequencedCollection},
+ * but the ordering applies to mappings instead of individual elements.
+ * <p>
+ * The bulk operations on this map, including the {@link #forEach forEach} and the
+ * {@link #replaceAll replaceAll} methods, operate on this map's mappings in
+ * encounter order.
+ * <p>
+ * The view collections provided by the
+ * {@link #keySet keySet},
+ * {@link #values values},
+ * {@link #entrySet entrySet},
+ * {@link #sequencedKeySet sequencedKeySet},
+ * {@link #sequencedValues sequencedValues},
+ * and
+ * {@link #sequencedEntrySet sequencedEntrySet} methods all reflect the encounter order
+ * of this map. Even though the return values of the {@code keySet}, {@code values}, and
+ * {@code entrySet} methods are not sequenced <i>types</i>, the elements
+ * in those view collections do reflect the encounter order of this map. Thus, the
+ * iterators returned by the statements
+ * {@snippet :
+ *     var it1 = sequencedMap.entrySet().iterator();
+ *     var it2 = sequencedMap.sequencedEntrySet().iterator();
+ * }
+ * both provide the mappings of {@code sequencedMap} in that map's encounter order.
+ * <p>
+ * This interface provides methods to add mappings, to retrieve mappings, and to remove
+ * mappings at either end of the map's encounter order.
+ * <p>
+ * This interface also defines the {@link #reversed} method, which provides a
+ * reverse-ordered <a href="Collection.html#view">view</a> of this map.
+ * In the reverse-ordered view, the concepts of first and last are inverted, as
+ * are the concepts of successor and predecessor. The first mapping of this map
+ * is the last mapping of the reverse-ordered view, and vice-versa. The successor of some
+ * mapping in this map is its predecessor in the reversed view, and vice-versa. All
+ * methods that respect the encounter order of the map operate as if the encounter order
+ * is inverted. For instance, the {@link #forEach forEach} method of the reversed view reports
+ * the mappings in order from the last mapping of this map to the first. In addition, all of
+ * the view collections of the reversed view also reflect the inverse of this map's
+ * encounter order. For example,
+ * {@snippet :
+ *     var itr = sequencedMap.reversed().entrySet().iterator();
+ * }
+ * provides the mappings of this map in the inverse of the encounter order, that is, from
+ * the last mapping to the first mapping. The availability of the {@code reversed} method,
+ * and its impact on the ordering semantics of all applicable methods and views, allow convenient
+ * iteration, searching, copying, and streaming of this map's mappings in either forward order or
+ * reverse order.
+ * <p>
+ * A map's reverse-ordered view is generally not serializable, even if the original
+ * map is serializable.
+ * <p>
+ * The {@link Map.Entry} instances obtained by iterating the {@link #entrySet} view, the
+ * {@link #sequencedEntrySet} view, and its reverse-ordered view, maintain a connection to the
+ * underlying map. This connection is guaranteed only during the iteration. It is unspecified
+ * whether the connection is maintained outside of the iteration. If the underlying map permits
+ * it, calling an Entry's {@link Map.Entry#setValue setValue} method will modify the value of the
+ * underlying mapping. It is, however, unspecified whether modifications to the value in the
+ * underlying mapping are visible in the {@code Entry} instance.
+ * <p>
+ * The methods
+ * {@link #firstEntry},
+ * {@link #lastEntry},
+ * {@link #pollFirstEntry}, and
+ * {@link #pollLastEntry}
+ * return {@link Map.Entry} instances that represent snapshots of mappings as
+ * of the time of the call. They do <em>not</em> support mutation of the
+ * underlying map via the optional {@link Map.Entry#setValue setValue} method.
+ * <p>
+ * Depending upon the implementation, the {@code Entry} instances returned by other
+ * means might or might not be connected to the underlying map. For example, consider
+ * an {@code Entry} obtained in the following manner:
+ * {@snippet :
+ *     var entry = sequencedMap.sequencedEntrySet().getFirst();
+ * }
+ * It is not specified by this interface whether the {@code setValue} method of the
+ * {@code Entry} thus obtained will update a mapping in the underlying map, or whether
+ * it will throw an exception, or whether changes to the underlying map are visible in
+ * that {@code Entry}.
+ * <p>
+ * This interface has the same requirements on the {@code equals} and {@code hashCode}
+ * methods as defined by {@link Map#equals Map.equals} and {@link Map#hashCode Map.hashCode}.
+ * Thus, a {@code Map} and a {@code SequencedMap} will compare equals if and only
+ * if they have equal mappings, irrespective of ordering.
+ * <p>
+ * This class is a member of the
+ * <a href="{@docRoot}/java.base/java/util/package-summary.html#CollectionsFramework">
+ * Java Collections Framework</a>.
+ *
+ * @param <K> the type of keys maintained by this map
+ * @param <V> the type of mapped values
+ * @since 21
+ */
+public interface SequencedMap<K, V> extends Map<K, V> {
+    /**
+     * Returns a reverse-ordered <a href="Collection.html#view">view</a> of this map.
+     * The encounter order of mappings in the returned view is the inverse of the encounter
+     * order of mappings in this map. The reverse ordering affects all order-sensitive operations,
+     * including those on the view collections of the returned view. If the implementation permits
+     * modifications to this view, the modifications "write through" to the underlying map.
+     * Changes to the underlying map might or might not be visible in this reversed view,
+     * depending upon the implementation.
+     *
+     * @return a reverse-ordered view of this map
+     */
+    SequencedMap<K, V> reversed();
+
+    /**
+     * Returns the first key-value mapping in this map,
+     * or {@code null} if the map is empty.
+     *
+     * @implSpec
+     * The implementation in this interface obtains the iterator of this map's entrySet.
+     * If the iterator has an element, it returns an unmodifiable copy of that element.
+     * Otherwise, it returns null.
+     *
+     * @return the first key-value mapping,
+     *         or {@code null} if this map is empty
+     */
+    default Map.Entry<K,V> firstEntry() {
+        var it = entrySet().iterator();
+        return it.hasNext() ? Map.Entry.copyOf(it.next()) : null;
+    }
+
+    /**
+     * Returns the last key-value mapping in this map,
+     * or {@code null} if the map is empty.
+     *
+     * @implSpec
+     * The implementation in this interface obtains the iterator of the entrySet of this map's
+     * reversed view. If the iterator has an element, it returns an unmodifiable copy of
+     * that element. Otherwise, it returns null.
+     *
+     * @return the last key-value mapping,
+     *         or {@code null} if this map is empty
+     */
+    default Map.Entry<K,V> lastEntry() {
+        var it = reversed().entrySet().iterator();
+        return it.hasNext() ? Map.Entry.copyOf(it.next()) : null;
+    }
+
+    /**
+     * Removes and returns the first key-value mapping in this map,
+     * or {@code null} if the map is empty (optional operation).
+     *
+     * @implSpec
+     * The implementation in this interface obtains the iterator of this map's entrySet.
+     * If the iterator has an element, it calls {@code remove} on the iterator and
+     * then returns an unmodifiable copy of that element. Otherwise, it returns null.
+     *
+     * @return the removed first entry of this map,
+     *         or {@code null} if this map is empty
+     * @throws UnsupportedOperationException if this collection implementation does not
+     *         support this operation
+     */
+    default Map.Entry<K,V> pollFirstEntry() {
+        var it = entrySet().iterator();
+        if (it.hasNext()) {
+            var entry = Map.Entry.copyOf(it.next());
+            it.remove();
+            return entry;
+        } else {
+            return null;
+        }
+    }
+
+    /**
+     * Removes and returns the last key-value mapping in this map,
+     * or {@code null} if the map is empty (optional operation).
+     *
+     * @implSpec
+     * The implementation in this interface obtains the iterator of the entrySet of this map's
+     * reversed view. If the iterator has an element, it calls {@code remove} on the iterator
+     * and then returns an unmodifiable copy of that element. Otherwise, it returns null.
+     *
+     * @return the removed last entry of this map,
+     *         or {@code null} if this map is empty
+     * @throws UnsupportedOperationException if this collection implementation does not
+     *         support this operation
+     */
+    default Map.Entry<K,V> pollLastEntry() {
+        var it = reversed().entrySet().iterator();
+        if (it.hasNext()) {
+            var entry = Map.Entry.copyOf(it.next());
+            it.remove();
+            return entry;
+        } else {
+            return null;
+        }
+    }
+
+    /**
+     * Inserts the given mapping into the map if it is not already present, or replaces the
+     * value of a mapping if it is already present (optional operation). After this operation
+     * completes normally, the given mapping will be present in this map, and it will be the
+     * first mapping in this map's encounter order.
+     *
+     * @implSpec The implementation in this interface always throws
+     * {@code UnsupportedOperationException}.
+     *
+     * @param k the key
+     * @param v the value
+     * @return the value previously associated with k, or null if none
+     * @throws UnsupportedOperationException if this collection implementation does not
+     *         support this operation
+     */
+    default V putFirst(K k, V v) {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * Inserts the given mapping into the map if it is not already present, or replaces the
+     * value of a mapping if it is already present (optional operation). After this operation
+     * completes normally, the given mapping will be present in this map, and it will be the
+     * last mapping in this map's encounter order.
+     *
+     * @implSpec The implementation in this interface always throws
+     * {@code UnsupportedOperationException}.
+     *
+     * @param k the key
+     * @param v the value
+     * @return the value previously associated with k, or null if none
+     * @throws UnsupportedOperationException if this collection implementation does not
+     *         support this operation
+     */
+    default V putLast(K k, V v) {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * Returns a {@link SequencedSet} view of this map's keySet.
+     *
+     * @implSpec
+     * The implementation in this interface returns a {@code SequencedSet}
+     * implementation that delegates all operations either to this map or to this map's
+     * {@link #keySet}, except for its {@link SequencedSet#reversed reversed} method,
+     * which instead returns the result of calling {@code sequencedKeySet} on this map's
+     * reverse-ordered view.
+     *
+     * @return a SequencedSet view of this map's keySet
+     */
+    default SequencedSet<K> sequencedKeySet() {
+        class SeqKeySet extends AbstractMap.ViewCollection<K> implements SequencedSet<K> {
+            SeqKeySet() {
+                super(SequencedMap.this.keySet());
+            }
+            public SequencedSet<K> reversed() {
+                return SequencedMap.this.reversed().sequencedKeySet();
+            }
+        }
+        return new SeqKeySet();
+    }
+
+    /**
+     * Returns a {@link SequencedCollection} view of this map's values collection.
+     *
+     * @implSpec
+     * The implementation in this interface returns a {@code SequencedCollection}
+     * implementation that delegates all operations either to this map or to this map's
+     * {@link #values} collection, except for its {@link SequencedCollection#reversed reversed}
+     * method, which instead returns the result of calling {@code sequencedValues} on this map's
+     * reverse-ordered view.
+     *
+     * @return a SequencedCollection view of this map's values collection
+     */
+    default SequencedCollection<V> sequencedValues() {
+        class SeqValues extends AbstractMap.ViewCollection<V> implements SequencedCollection<V> {
+            SeqValues() {
+                super(SequencedMap.this.values());
+            }
+            public SequencedCollection<V> reversed() {
+                return SequencedMap.this.reversed().sequencedValues();
+            }
+        }
+        return new SeqValues();
+    }
+
+    /**
+     * Returns a {@link SequencedSet} view of this map's entrySet.
+     *
+     * @implSpec
+     * The implementation in this interface returns a {@code SequencedSet}
+     * implementation that delegates all operations either to this map or to this map's
+     * {@link #entrySet}, except for its {@link SequencedSet#reversed reversed} method,
+     * which instead returns the result of calling {@code sequencedEntrySet} on this map's
+     * reverse-ordered view.
+     *
+     * @return a SequencedSet view of this map's entrySet
+     */
+    default SequencedSet<Map.Entry<K, V>> sequencedEntrySet() {
+        class SeqEntrySet extends AbstractMap.ViewCollection<Map.Entry<K, V>>
+                implements SequencedSet<Map.Entry<K, V>> {
+            SeqEntrySet() {
+                super(SequencedMap.this.entrySet());
+            }
+            public SequencedSet<Map.Entry<K, V>> reversed() {
+                return SequencedMap.this.reversed().sequencedEntrySet();
+            }
+        }
+        return new SeqEntrySet();
+    }
+}
diff --git a/src/java.base/share/classes/java/util/SequencedSet.java b/src/java.base/share/classes/java/util/SequencedSet.java
new file mode 100644
index 00000000000..c02bfc123ff
--- /dev/null
+++ b/src/java.base/share/classes/java/util/SequencedSet.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2021, 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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package java.util;
+
+/**
+ * A collection that is both a {@link SequencedCollection} and a {@link Set}. As such,
+ * it can be thought of either as a {@code Set} that also has a well-defined
+ * <a href="SequencedCollection.html#encounter">encounter order</a>, or as a
+ * {@code SequencedCollection} that also has unique elements.
+ * <p>
+ * This interface has the same requirements on the {@code equals} and {@code hashCode}
+ * methods as defined by {@link Set#equals Set.equals} and {@link Set#hashCode Set.hashCode}.
+ * Thus, a {@code Set} and a {@code SequencedSet} will compare equals if and only
+ * if they have equal elements, irrespective of ordering.
+ * <p>
+ * {@code SequencedSet} defines the {@link #reversed} method, which provides a
+ * reverse-ordered <a href="Collection.html#view">view</a> of this set. The only difference
+ * from the {@link SequencedCollection#reversed SequencedCollection.reversed} method is
+ * that the return type of {@code SequencedSet.reversed} is {@code SequencedSet}.
+ * <p>
+ * This class is a member of the
+ * <a href="{@docRoot}/java.base/java/util/package-summary.html#CollectionsFramework">
+ * Java Collections Framework</a>.
+ *
+ * @param <E> the type of elements in this sequenced set
+ * @since 21
+ */
+public interface SequencedSet<E> extends SequencedCollection<E>, Set<E> {
+    /**
+     * {@inheritDoc}
+     *
+     * @return a reverse-ordered view of this collection, as a {@code SequencedSet}
+     */
+    SequencedSet<E> reversed();
+}
diff --git a/src/java.base/share/classes/java/util/SortedMap.java b/src/java.base/share/classes/java/util/SortedMap.java
index 77553bcc578..1af9d59e83e 100644
--- a/src/java.base/share/classes/java/util/SortedMap.java
+++ b/src/java.base/share/classes/java/util/SortedMap.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1998, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1998, 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
@@ -110,7 +110,7 @@ package java.util;
  * @since 1.2
  */
 
-public interface SortedMap<K,V> extends Map<K,V> {
+public interface SortedMap<K,V> extends SequencedMap<K,V> {
     /**
      * Returns the comparator used to order the keys in this map, or
      * {@code null} if this map uses the {@linkplain Comparable
@@ -281,4 +281,48 @@ public interface SortedMap<K,V> extends Map<K,V> {
      *         sorted in ascending key order
      */
     Set<Map.Entry<K, V>> entrySet();
+
+    /**
+     * Throws {@code UnsupportedOperationException}. The encounter order induced by this
+     * map's comparison method determines the position of mappings, so explicit positioning
+     * is not supported.
+     *
+     * @implSpec
+     * The implementation in this interface always throws {@code UnsupportedOperationException}.
+     *
+     * @throws UnsupportedOperationException always
+     * @since 21
+     */
+     default V putFirst(K k, V v) {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * Throws {@code UnsupportedOperationException}. The encounter order induced by this
+     * map's comparison method determines the position of mappings, so explicit positioning
+     * is not supported.
+     *
+     * @implSpec
+     * The implementation in this interface always throws {@code UnsupportedOperationException}.
+     *
+     * @throws UnsupportedOperationException always
+     * @since 21
+     */
+    default V putLast(K k, V v) {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * @implSpec
+     * The implementation in this interface returns an instance of a reverse-ordered
+     * SortedMap that delegates its operations to this SortedMap.
+     *
+     * @return a reverse-ordered view of this map, as a {@code SortedMap}
+     * @since 21
+     */
+    default SortedMap<K, V> reversed() {
+        return ReverseOrderSortedMapView.of(this);
+    }
 }
diff --git a/src/java.base/share/classes/java/util/SortedSet.java b/src/java.base/share/classes/java/util/SortedSet.java
index c3ef264a2af..48473a1fed5 100644
--- a/src/java.base/share/classes/java/util/SortedSet.java
+++ b/src/java.base/share/classes/java/util/SortedSet.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1998, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1998, 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
@@ -105,7 +105,7 @@ package java.util;
  * @since 1.2
  */
 
-public interface SortedSet<E> extends Set<E> {
+public interface SortedSet<E> extends Set<E>, SequencedSet<E> {
     /**
      * Returns the comparator used to order the elements in this set,
      * or {@code null} if this set uses the {@linkplain Comparable
@@ -261,4 +261,112 @@ public interface SortedSet<E> extends Set<E> {
             }
         };
     }
+
+    // ========== SequencedCollection ==========
+
+    /**
+     * Throws {@code UnsupportedOperationException}. The encounter order induced by this
+     * set's comparison method determines the position of elements, so explicit positioning
+     * is not supported.
+     *
+     * @implSpec
+     * The implementation in this interface always throws {@code UnsupportedOperationException}.
+     *
+     * @throws UnsupportedOperationException always
+     * @since 21
+     */
+    default void addFirst(E e) {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * Throws {@code UnsupportedOperationException}. The encounter order induced by this
+     * set's comparison method determines the position of elements, so explicit positioning
+     * is not supported.
+     *
+     * @implSpec
+     * The implementation in this interface always throws {@code UnsupportedOperationException}.
+     *
+     * @throws UnsupportedOperationException always
+     * @since 21
+     */
+    default void addLast(E e) {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * @implSpec
+     * The implementation in this interface returns the result of calling the {@code first} method.
+     *
+     * @throws NoSuchElementException {@inheritDoc}
+     * @since 21
+     */
+    default E getFirst() {
+        return this.first();
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * @implSpec
+     * The implementation in this interface returns the result of calling the {@code last} method.
+     *
+     * @throws NoSuchElementException {@inheritDoc}
+     * @since 21
+     */
+    default E getLast() {
+        return this.last();
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * @implSpec
+     * The implementation in this interface calls the {@code first} method to obtain the first
+     * element, then it calls {@code remove(element)} to remove the element, and then it returns
+     * the element.
+     *
+     * @throws NoSuchElementException {@inheritDoc}
+     * @throws UnsupportedOperationException {@inheritDoc}
+     * @since 21
+     */
+    default E removeFirst() {
+        E e = this.first();
+        this.remove(e);
+        return e;
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * @implSpec
+     * The implementation in this interface calls the {@code last} method to obtain the last
+     * element, then it calls {@code remove(element)} to remove the element, and then it returns
+     * the element.
+     *
+     * @throws NoSuchElementException {@inheritDoc}
+     * @throws UnsupportedOperationException {@inheritDoc}
+     * @since 21
+     */
+    default E removeLast() {
+        E e = this.last();
+        this.remove(e);
+        return e;
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * @implSpec
+     * The implementation in this interface returns an instance of a reverse-ordered
+     * SortedSet that delegates its operations to this SortedSet.
+     *
+     * @return a reverse-ordered view of this collection, as a {@code SortedSet}
+     * @since 21
+     */
+    default SortedSet<E> reversed() {
+        return ReverseOrderSortedSetView.of(this);
+    }
 }
diff --git a/src/java.base/share/classes/java/util/TreeMap.java b/src/java.base/share/classes/java/util/TreeMap.java
index e33c95e44d2..a91a56a5900 100644
--- a/src/java.base/share/classes/java/util/TreeMap.java
+++ b/src/java.base/share/classes/java/util/TreeMap.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
@@ -99,6 +99,10 @@ import java.util.function.Function;
  * of the time of the call. They do <em>not</em> support mutation of the
  * underlying map via the optional {@link Map.Entry#setValue setValue} method.
  *
+ * <p>The {@link #putFirst putFirst} and {@link #putLast putLast} methods of this class
+ * throw {@code UnsupportedOperationException}. The encounter order of mappings is determined
+ * by the comparison method; therefore, explicit positioning is not supported.
+ *
  * <p>This class is a member of the
  * <a href="{@docRoot}/java.base/java/util/package-summary.html#CollectionsFramework">
  * Java Collections Framework</a>.
@@ -305,6 +309,30 @@ public class TreeMap<K,V>
         return key(getLastEntry());
     }
 
+    /**
+     * Throws {@code UnsupportedOperationException}. The encounter order induced by this
+     * map's comparison method determines the position of mappings, so explicit positioning
+     * is not supported.
+     *
+     * @throws UnsupportedOperationException always
+     * @since 21
+     */
+     public V putFirst(K k, V v) {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * Throws {@code UnsupportedOperationException}. The encounter order induced by this
+     * map's comparison method determines the position of mappings, so explicit positioning
+     * is not supported.
+     *
+     * @throws UnsupportedOperationException always
+     * @since 21
+     */
+    public V putLast(K k, V v) {
+        throw new UnsupportedOperationException();
+    }
+
     /**
      * Copies all of the mappings from the specified map to this map.
      * These mappings replace any mappings that this map had for any
diff --git a/src/java.base/share/classes/java/util/TreeSet.java b/src/java.base/share/classes/java/util/TreeSet.java
index f189bb4a9f2..a68c157283e 100644
--- a/src/java.base/share/classes/java/util/TreeSet.java
+++ b/src/java.base/share/classes/java/util/TreeSet.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1998, 2019, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1998, 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
@@ -73,6 +73,10 @@ package java.util;
  * exception for its correctness:   <i>the fail-fast behavior of iterators
  * should be used only to detect bugs.</i>
  *
+ * <p>The {@link #addFirst addFirst} and {@link #addLast addLast} methods of this class
+ * throw {@code UnsupportedOperationException}. The encounter order of elements is determined
+ * by the comparison method; therefore, explicit positioning is not supported.
+ *
  * <p>This class is a member of the
  * <a href="{@docRoot}/java.base/java/util/package-summary.html#CollectionsFramework">
  * Java Collections Framework</a>.
@@ -460,6 +464,30 @@ public class TreeSet<E> extends AbstractSet<E>
         return (e == null) ? null : e.getKey();
     }
 
+    /**
+     * Throws {@code UnsupportedOperationException}. The encounter order induced by this
+     * set's comparison method determines the position of elements, so explicit positioning
+     * is not supported.
+     *
+     * @throws UnsupportedOperationException always
+     * @since 21
+     */
+    public void addFirst(E e) {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * Throws {@code UnsupportedOperationException}. The encounter order induced by this
+     * set's comparison method determines the position of elements, so explicit positioning
+     * is not supported.
+     *
+     * @throws UnsupportedOperationException always
+     * @since 21
+     */
+    public void addLast(E e) {
+        throw new UnsupportedOperationException();
+    }
+
     /**
      * Returns a shallow copy of this {@code TreeSet} instance. (The elements
      * themselves are not cloned.)
diff --git a/src/java.base/share/classes/java/util/concurrent/ConcurrentSkipListMap.java b/src/java.base/share/classes/java/util/concurrent/ConcurrentSkipListMap.java
index 4c2add084ab..bccbccf5725 100644
--- a/src/java.base/share/classes/java/util/concurrent/ConcurrentSkipListMap.java
+++ b/src/java.base/share/classes/java/util/concurrent/ConcurrentSkipListMap.java
@@ -1870,6 +1870,30 @@ public class ConcurrentSkipListMap<K,V> extends AbstractMap<K,V>
         return n.key;
     }
 
+   /**
+     * Throws {@code UnsupportedOperationException}. The encounter order induced by this
+     * map's comparison method determines the position of mappings, so explicit positioning
+     * is not supported.
+     *
+     * @throws UnsupportedOperationException always
+     * @since 21
+     */
+     public V putFirst(K k, V v) {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * Throws {@code UnsupportedOperationException}. The encounter order induced by this
+     * map's comparison method determines the position of mappings, so explicit positioning
+     * is not supported.
+     *
+     * @throws UnsupportedOperationException always
+     * @since 21
+     */
+    public V putLast(K k, V v) {
+        throw new UnsupportedOperationException();
+    }
+
     /**
      * @throws ClassCastException {@inheritDoc}
      * @throws NullPointerException if {@code fromKey} or {@code toKey} is null
diff --git a/src/java.base/share/classes/java/util/concurrent/ConcurrentSkipListSet.java b/src/java.base/share/classes/java/util/concurrent/ConcurrentSkipListSet.java
index 4d79c421331..0db76e03e05 100644
--- a/src/java.base/share/classes/java/util/concurrent/ConcurrentSkipListSet.java
+++ b/src/java.base/share/classes/java/util/concurrent/ConcurrentSkipListSet.java
@@ -403,6 +403,30 @@ public class ConcurrentSkipListSet<E>
         return m.lastKey();
     }
 
+    /**
+     * Throws {@code UnsupportedOperationException}. The encounter order induced by this
+     * set's comparison method determines the position of elements, so explicit positioning
+     * is not supported.
+     *
+     * @throws UnsupportedOperationException always
+     * @since 21
+     */
+    public void addFirst(E e) {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * Throws {@code UnsupportedOperationException}. The encounter order induced by this
+     * set's comparison method determines the position of elements, so explicit positioning
+     * is not supported.
+     *
+     * @throws UnsupportedOperationException always
+     * @since 21
+     */
+    public void addLast(E e) {
+        throw new UnsupportedOperationException();
+    }
+
     /**
      * @throws ClassCastException {@inheritDoc}
      * @throws NullPointerException if {@code fromElement} or
diff --git a/src/java.base/share/classes/java/util/concurrent/CopyOnWriteArrayList.java b/src/java.base/share/classes/java/util/concurrent/CopyOnWriteArrayList.java
index ee8e7b81728..909621bd5ba 100644
--- a/src/java.base/share/classes/java/util/concurrent/CopyOnWriteArrayList.java
+++ b/src/java.base/share/classes/java/util/concurrent/CopyOnWriteArrayList.java
@@ -39,6 +39,7 @@ import java.lang.reflect.Field;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
+import java.util.Collections;
 import java.util.Comparator;
 import java.util.ConcurrentModificationException;
 import java.util.Iterator;
@@ -50,9 +51,13 @@ import java.util.RandomAccess;
 import java.util.Spliterator;
 import java.util.Spliterators;
 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;
+import java.util.stream.StreamSupport;
 import jdk.internal.access.SharedSecrets;
+import jdk.internal.util.ArraysSupport;
 
 /**
  * A thread-safe variant of {@link java.util.ArrayList} in which all mutative
@@ -398,6 +403,34 @@ public class CopyOnWriteArrayList<E>
         return elementAt(getArray(), index);
     }
 
+    /**
+     * {@inheritDoc}
+     *
+     * @throws NoSuchElementException {@inheritDoc}
+     * @since 21
+     */
+    public E getFirst() {
+        Object[] es = getArray();
+        if (es.length == 0)
+            throw new NoSuchElementException();
+        else
+            return elementAt(es, 0);
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * @throws NoSuchElementException {@inheritDoc}
+     * @since 21
+     */
+    public E getLast() {
+        Object[] es = getArray();
+        if (es.length == 0)
+            throw new NoSuchElementException();
+        else
+            return elementAt(es, es.length - 1);
+    }
+
     /**
      * Replaces the element at the specified position in this list with the
      * specified element.
@@ -464,6 +497,26 @@ public class CopyOnWriteArrayList<E>
         }
     }
 
+    /**
+     * {@inheritDoc}
+     *
+     * @since 21
+     */
+    public void addFirst(E e) {
+        add(0, e);
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * @since 21
+     */
+    public void addLast(E e) {
+        synchronized (lock) {
+            add(getArray().length, e);
+        }
+    }
+
     /**
      * Removes the element at the specified position in this list.
      * Shifts any subsequent elements to the left (subtracts one from their
@@ -491,6 +544,37 @@ public class CopyOnWriteArrayList<E>
         }
     }
 
+    /**
+     * {@inheritDoc}
+     *
+     * @throws NoSuchElementException {@inheritDoc}
+     * @since 21
+     */
+    public E removeFirst() {
+        synchronized (lock) {
+            if (getArray().length == 0)
+                throw new NoSuchElementException();
+            else
+                return remove(0);
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * @throws NoSuchElementException {@inheritDoc}
+     * @since 21
+     */
+    public E removeLast() {
+        synchronized (lock) {
+            int size = getArray().length;
+            if (size == 0)
+                throw new NoSuchElementException();
+            else
+                return remove(size - 1);
+        }
+    }
+
     /**
      * Removes the first occurrence of the specified element from this list,
      * if it is present.  If this list does not contain the element, it is
@@ -1358,6 +1442,24 @@ public class CopyOnWriteArrayList<E>
             }
         }
 
+        public E getFirst() {
+            synchronized (lock) {
+                if (size == 0)
+                    throw new NoSuchElementException();
+                else
+                    return get(0);
+            }
+        }
+
+        public E getLast() {
+            synchronized (lock) {
+                if (size == 0)
+                    throw new NoSuchElementException();
+                else
+                    return get(size - 1);
+            }
+        }
+
         public int size() {
             synchronized (lock) {
                 checkForComodification();
@@ -1385,6 +1487,16 @@ public class CopyOnWriteArrayList<E>
             }
         }
 
+        public void addFirst(E e) {
+            add(0, e);
+        }
+
+        public void addLast(E e) {
+            synchronized (lock) {
+                add(size, e);
+            }
+        }
+
         public boolean addAll(Collection<? extends E> c) {
             synchronized (lock) {
                 final Object[] oldArray = getArrayChecked();
@@ -1426,6 +1538,24 @@ public class CopyOnWriteArrayList<E>
             }
         }
 
+        public E removeFirst() {
+            synchronized (lock) {
+                if (size == 0)
+                    throw new NoSuchElementException();
+                else
+                    return remove(0);
+            }
+        }
+
+        public E removeLast() {
+            synchronized (lock) {
+                if (size == 0)
+                    throw new NoSuchElementException();
+                else
+                    return remove(size - 1);
+            }
+        }
+
         public boolean remove(Object o) {
             synchronized (lock) {
                 checkForComodification();
@@ -1524,6 +1654,9 @@ public class CopyOnWriteArrayList<E>
             }
         }
 
+        public List<E> reversed() {
+            return new Reversed<>(this, lock);
+        }
     }
 
     private static class COWSubListIterator<E> implements ListIterator<E> {
@@ -1589,6 +1722,365 @@ public class CopyOnWriteArrayList<E>
         }
     }
 
+    /**
+     * {@inheritDoc}
+     * <p>
+     * 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. Sublists and iterators of the reversed view have
+     * the same restrictions as those of this list.
+     *
+     * @since 21
+     */
+    public List<E> reversed() {
+        return new Reversed<>(this, lock);
+    }
+
+    /**
+     * Reversed view for CopyOnWriteArrayList and its sublists.
+     */
+    private static class Reversed<E> implements List<E>, RandomAccess {
+        final List<E> base;
+        final Object lock;
+
+        Reversed(List<E> base, Object lock) {
+            this.base = base;
+            this.lock = lock;
+        }
+
+        class DescendingIterator implements Iterator<E> {
+            final ListIterator<E> it;
+            DescendingIterator() {
+                synchronized (lock) {
+                    it = base.listIterator(base.size());
+                }
+            }
+            public boolean hasNext() { return it.hasPrevious(); }
+            public E next() { return it.previous(); }
+            public void remove() { it.remove(); }
+        }
+
+        class DescendingListIterator implements ListIterator<E> {
+            final ListIterator<E> it;
+            final int size; // iterator holds a snapshot of the array so this is constant
+
+            DescendingListIterator(int pos) {
+                synchronized (lock) {
+                    size = base.size();
+                    if (pos < 0 || pos > size)
+                        throw new IndexOutOfBoundsException();
+                    it = base.listIterator(size - pos);
+                }
+            }
+
+            public boolean hasNext() {
+                return it.hasPrevious();
+            }
+
+            public E next() {
+                return it.previous();
+            }
+
+            public boolean hasPrevious() {
+                return it.hasNext();
+            }
+
+            public E previous() {
+                return it.next();
+            }
+
+            public int nextIndex() {
+                return size - it.nextIndex();
+            }
+
+            public int previousIndex() {
+                return nextIndex() - 1;
+            }
+
+            public void remove() {
+                throw new UnsupportedOperationException();
+            }
+
+            public void set(E e) {
+                throw new UnsupportedOperationException();
+            }
+
+            public void add(E e) {
+                throw new UnsupportedOperationException();
+            }
+        }
+
+        // ========== Iterable ==========
+
+        public void forEach(Consumer<? super E> action) {
+            for (E e : this)
+                action.accept(e);
+        }
+
+        public Iterator<E> iterator() {
+            return new DescendingIterator();
+        }
+
+        public Spliterator<E> spliterator() {
+            // TODO can probably improve this
+            return Spliterators.spliteratorUnknownSize(new DescendingIterator(), 0);
+        }
+
+        // ========== Collection ==========
+
+        public boolean add(E e) {
+            base.add(0, e);
+            return true;
+        }
+
+        public boolean addAll(Collection<? extends E> c) {
+            @SuppressWarnings("unchecked")
+            E[] es = (E[]) c.toArray();
+            if (es.length > 0) {
+                ArraysSupport.reverse(es);
+                base.addAll(0, Arrays.asList(es));
+                return true;
+            } else {
+                return false;
+            }
+        }
+
+        public void clear() {
+            base.clear();
+        }
+
+        public boolean contains(Object o) {
+            return base.contains(o);
+        }
+
+        public boolean containsAll(Collection<?> c) {
+            return base.containsAll(c);
+        }
+
+        // copied from AbstractList
+        public boolean equals(Object o) {
+            if (o == this)
+                return true;
+            if (!(o instanceof List))
+                return false;
+
+            ListIterator<E> e1 = listIterator();
+            ListIterator<?> e2 = ((List<?>) o).listIterator();
+            while (e1.hasNext() && e2.hasNext()) {
+                E o1 = e1.next();
+                Object o2 = e2.next();
+                if (!(o1==null ? o2==null : o1.equals(o2)))
+                    return false;
+            }
+            return !(e1.hasNext() || e2.hasNext());
+        }
+
+        // copied from AbstractList
+        public int hashCode() {
+            int hashCode = 1;
+            for (E e : this)
+                hashCode = 31*hashCode + (e==null ? 0 : e.hashCode());
+            return hashCode;
+        }
+
+        public boolean isEmpty() {
+            return base.isEmpty();
+        }
+
+        public Stream<E> parallelStream() {
+            return StreamSupport.stream(spliterator(), true);
+        }
+
+        public boolean remove(Object o) {
+            synchronized (lock) {
+                int index = indexOf(o);
+                if (index == -1)
+                    return false;
+                remove(index);
+                return true;
+            }
+        }
+
+        public boolean removeAll(Collection<?> c) {
+            return base.removeAll(c);
+        }
+
+        public boolean retainAll(Collection<?> c) {
+            return base.retainAll(c);
+        }
+
+        public int size() {
+            return base.size();
+        }
+
+        public Stream<E> stream() {
+            return StreamSupport.stream(spliterator(), false);
+        }
+
+        public Object[] toArray() {
+            return ArraysSupport.reverse(base.toArray());
+        }
+
+        @SuppressWarnings("unchecked")
+        public <T> T[] toArray(T[] a) {
+            // TODO optimize this
+            return toArray(i -> (T[]) java.lang.reflect.Array.newInstance(a.getClass().getComponentType(), i));
+        }
+
+        public <T> T[] toArray(IntFunction<T[]> generator) {
+            return ArraysSupport.reverse(base.toArray(generator));
+        }
+
+        // copied from AbstractCollection
+        public String toString() {
+            Iterator<E> it = iterator();
+            if (! it.hasNext())
+                return "[]";
+
+            StringBuilder sb = new StringBuilder();
+            sb.append('[');
+            for (;;) {
+                E e = it.next();
+                sb.append(e == this ? "(this Collection)" : e);
+                if (! it.hasNext())
+                    return sb.append(']').toString();
+                sb.append(',').append(' ');
+            }
+        }
+
+        // ========== List ==========
+
+        public void add(int index, E element) {
+            synchronized (lock) {
+                base.add(base.size() - index, element);
+            }
+        }
+
+        public void addFirst(E e) {
+            base.add(e);
+        }
+
+        public void addLast(E e) {
+            base.add(0, e);
+        }
+
+        public boolean addAll(int index, Collection<? extends E> c) {
+            @SuppressWarnings("unchecked")
+            E[] es = (E[]) c.toArray();
+            if (es.length > 0) {
+                ArraysSupport.reverse(es);
+                synchronized (lock) {
+                    base.addAll(base.size() - index, Arrays.asList(es));
+                }
+                return true;
+            } else {
+                return false;
+            }
+        }
+
+        public E get(int i) {
+            synchronized (lock) {
+                return base.get(base.size() - i - 1);
+            }
+        }
+
+        public E getFirst() {
+            synchronized (lock) {
+                int size = base.size();
+                if (size == 0)
+                    throw new NoSuchElementException();
+                else
+                    return base.get(size - 1);
+            }
+        }
+
+        public E getLast() {
+            synchronized (lock) {
+                if (base.size() == 0)
+                    throw new NoSuchElementException();
+                else
+                    return base.get(0);
+            }
+        }
+
+        public int indexOf(Object o) {
+            synchronized (lock) {
+                int i = base.lastIndexOf(o);
+                return i == -1 ? -1 : base.size() - i - 1;
+            }
+        }
+
+        public int lastIndexOf(Object o) {
+            synchronized (lock) {
+                int i = base.indexOf(o);
+                return i == -1 ? -1 : base.size() - i - 1;
+            }
+        }
+
+        public ListIterator<E> listIterator() {
+            return new DescendingListIterator(0);
+        }
+
+        public ListIterator<E> listIterator(int index) {
+            return new DescendingListIterator(index);
+        }
+
+        public E remove(int index) {
+            synchronized (lock) {
+                return base.remove(base.size() - index - 1);
+            }
+        }
+
+        public E removeFirst() {
+            synchronized (lock) {
+                int size = base.size();
+                if (size == 0)
+                    throw new NoSuchElementException();
+                else
+                    return base.remove(size - 1);
+            }
+        }
+
+        public E removeLast() {
+            synchronized (lock) {
+                if (base.size() == 0)
+                    throw new NoSuchElementException();
+                else
+                    return base.remove(0);
+            }
+        }
+
+        public boolean removeIf(Predicate<? super E> filter) {
+            return base.removeIf(filter);
+        }
+
+        public void replaceAll(UnaryOperator<E> operator) {
+            base.replaceAll(operator);
+        }
+
+        public void sort(Comparator<? super E> c) {
+            base.sort(Collections.reverseOrder(c));
+        }
+
+        public E set(int index, E element) {
+            synchronized (lock) {
+                return base.set(base.size() - index - 1, element);
+            }
+        }
+
+        public List<E> subList(int fromIndex, int toIndex) {
+            synchronized (lock) {
+                int size = base.size();
+                var sub = base.subList(size - toIndex, size - fromIndex);
+                return new Reversed<>(sub, lock);
+            }
+        }
+
+        public List<E> reversed() {
+            return base;
+        }
+    }
+
     /** Initializes the lock; for use when deserializing or cloning. */
     private void resetLock() {
         @SuppressWarnings("removal")
diff --git a/src/java.base/share/classes/jdk/internal/util/ArraysSupport.java b/src/java.base/share/classes/jdk/internal/util/ArraysSupport.java
index b9d0e29e449..61be28519fc 100644
--- a/src/java.base/share/classes/jdk/internal/util/ArraysSupport.java
+++ b/src/java.base/share/classes/jdk/internal/util/ArraysSupport.java
@@ -24,6 +24,8 @@
  */
 package jdk.internal.util;
 
+import java.util.Arrays;
+import java.util.Collection;
 import jdk.internal.access.JavaLangAccess;
 import jdk.internal.access.SharedSecrets;
 import jdk.internal.misc.Unsafe;
@@ -755,4 +757,53 @@ public class ArraysSupport {
             return minLength;
         }
     }
+
+    /**
+     * Reverses the elements of an array in-place.
+     *
+     * @param <T> the array component type
+     * @param a the array to be reversed
+     * @return the reversed array, always the same array as the argument
+     */
+    public static <T> T[] reverse(T[] a) {
+        int limit = a.length / 2;
+        for (int i = 0, j = a.length - 1; i < limit; i++, j--) {
+            T t = a[i];
+            a[i] = a[j];
+            a[j] = t;
+        }
+        return a;
+    }
+
+    /**
+     * Dump the contents of the given collection into the given array, in reverse order.
+     * This mirrors the semantics of Collection.toArray(T[]) in regard to reusing the given
+     * array, appending null if necessary, or allocating a new array of the same component type.
+     * <p>
+     * A constraint is that this method should issue exactly one method call on the collection
+     * to obtain the elements and the size. Having a separate size() call or using an Iterator
+     * could result in errors if the collection changes size between calls. This implies that
+     * the elements need to be obtained via a single call to one of the toArray() methods.
+     * This further implies allocating memory proportional to the number of elements and
+     * making an extra copy, but this seems unavoidable.
+     * <p>
+     * An obvious approach would be simply to call coll.toArray(array) and then reverse the
+     * order of the elements. This doesn't work, because if given array is sufficiently long,
+     * we cannot tell how many elements were copied into it and thus there is no way to reverse
+     * the right set of elements while leaving the remaining array elements undisturbed.
+     *
+     * @throws ArrayStoreException if coll contains elements that can't be stored in the array
+     */
+    public static <T> T[] toArrayReversed(Collection<?> coll, T[] array) {
+        T[] newArray = reverse(coll.toArray(Arrays.copyOfRange(array, 0, 0)));
+        if (newArray.length > array.length) {
+            return newArray;
+        } else {
+            System.arraycopy(newArray, 0, array, 0, newArray.length);
+            if (array.length > newArray.length) {
+                array[newArray.length] = null;
+            }
+            return array;
+        }
+    }
 }
diff --git a/test/jdk/TEST.groups b/test/jdk/TEST.groups
index c4a9fe9dcd7..035f855a728 100644
--- a/test/jdk/TEST.groups
+++ b/test/jdk/TEST.groups
@@ -171,6 +171,7 @@ jdk_collections_core = \
     java/util/Map \
     java/util/NavigableMap \
     java/util/PriorityQueue \
+    java/util/SequencedCollection \
     java/util/TimSort \
     java/util/TreeMap \
     java/util/Vector \
diff --git a/test/jdk/java/util/Collection/MOAT.java b/test/jdk/java/util/Collection/MOAT.java
index ccaca2fc934..bbc332cc211 100644
--- a/test/jdk/java/util/Collection/MOAT.java
+++ b/test/jdk/java/util/Collection/MOAT.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2005, 2020, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2005, 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
@@ -137,9 +137,13 @@ public class MOAT {
         testMap(Collections.synchronizedNavigableMap(new TreeMap<Integer,Integer>()));
 
         // Unmodifiable wrappers
-        testImmutableSet(unmodifiableSet(new HashSet<>(Arrays.asList(1,2,3))));
+        testImmutableSet(unmodifiableSet(new HashSet<>(Arrays.asList(1,2,3))), 99);
         testImmutableList(unmodifiableList(Arrays.asList(1,2,3)));
         testImmutableMap(unmodifiableMap(Collections.singletonMap(1,2)));
+        testImmutableSeqColl(unmodifiableSequencedCollection(Arrays.asList(1,2,3)), 99);
+        testImmutableSeqColl(unmodifiableSequencedSet(new LinkedHashSet<>(Arrays.asList(1,2,3))), 99);
+        var lhm = new LinkedHashMap<Integer,Integer>(); lhm.put(1,2); lhm.put(3, 4);
+        testImmutableSeqMap(unmodifiableSequencedMap(lhm));
         testCollMutatorsAlwaysThrow(unmodifiableSet(new HashSet<>(Arrays.asList(1,2,3))));
         testCollMutatorsAlwaysThrow(unmodifiableSet(Collections.emptySet()));
         testEmptyCollMutatorsAlwaysThrow(unmodifiableSet(Collections.emptySet()));
@@ -169,7 +173,7 @@ public class MOAT {
         testEmptySet(Collections.emptySet());
         testEmptySet(Collections.emptySortedSet());
         testEmptySet(Collections.emptyNavigableSet());
-        testImmutableSet(emptySet);
+        testImmutableSet(emptySet, 99);
 
         List<Integer> emptyList = emptyList();
         testCollection(emptyList);
@@ -194,7 +198,7 @@ public class MOAT {
         Set<Integer> singletonSet = singleton(1);
         equal(singletonSet.size(), 1);
         testCollection(singletonSet);
-        testImmutableSet(singletonSet);
+        testImmutableSet(singletonSet, 99);
 
         List<Integer> singletonList = singletonList(1);
         equal(singletonList.size(), 1);
@@ -322,20 +326,20 @@ public class MOAT {
                 Set.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10),
                 Set.of(integerArray))) {
             testCollection(set);
-            testImmutableSet(set);
+            testImmutableSet(set, 99);
             testCollMutatorsAlwaysThrow(set);
         }
 
         Set<Integer> setCopy = Set.copyOf(Arrays.asList(1, 2, 3));
         testCollection(setCopy);
-        testImmutableSet(setCopy);
+        testImmutableSet(setCopy, 99);
         testCollMutatorsAlwaysThrow(setCopy);
 
         Set<Integer> setCollected = Stream.of(1, 1, 2, 3, 2, 3)
                                           .collect(Collectors.toUnmodifiableSet());
         equal(setCollected, Set.of(1, 2, 3));
         testCollection(setCollected);
-        testImmutableSet(setCollected);
+        testImmutableSet(setCollected, 99);
         testCollMutatorsAlwaysThrow(setCollected);
 
         // Immutable Map
@@ -462,12 +466,12 @@ public class MOAT {
             testEmptyIterator(((NavigableSet<T>)c).descendingIterator());
     }
 
-    private static void testImmutableCollection(final Collection<Integer> c) {
+    private static <T> void testImmutableCollection(final Collection<T> c, T t) {
         THROWS(UnsupportedOperationException.class,
-               () -> c.add(99),
-               () -> c.addAll(singleton(99)));
+               () -> c.add(t),
+               () -> c.addAll(singleton(t)));
         if (! c.isEmpty()) {
-            final Integer first = c.iterator().next();
+            final T first = c.iterator().next();
             THROWS(UnsupportedOperationException.class,
                    () -> c.clear(),
                    () -> c.remove(first),
@@ -476,13 +480,36 @@ public class MOAT {
         }
     }
 
-    private static void testImmutableSet(final Set<Integer> c) {
-        testImmutableCollection(c);
+    private static <T> void testImmutableSeqColl(final SequencedCollection<T> c, T t) {
+        SequencedCollection<T> r = c.reversed();
+        testImmutableCollection(c, t);
+        testImmutableCollection(r, t);
+        THROWS(UnsupportedOperationException.class,
+               () -> c.addFirst(t),
+               () -> c.addLast(t),
+               () -> r.addFirst(t),
+               () -> r.addLast(t));
+        if (! c.isEmpty()) {
+            THROWS(UnsupportedOperationException.class,
+                   () -> c.removeFirst(),
+                   () -> c.removeLast(),
+                   () -> r.removeFirst(),
+                   () -> r.removeLast());
+        }
+    }
+
+    private static <T> void testImmutableSet(final Set<T> c, T t) {
+        testImmutableCollection(c, t);
+    }
+
+    private static <T> void testImmutableSeqSet(final SequencedSet<T> c, T t) {
+        testImmutableSeqColl(c, t);
     }
 
     private static void testImmutableList(final List<Integer> c) {
         testList(c);
-        testImmutableCollection(c);
+        testImmutableCollection(c, 42);
+        testImmutableSeqColl(c, 42);
         THROWS(UnsupportedOperationException.class,
                () -> c.set(0,42),
                () -> c.add(0,42),
@@ -606,6 +633,15 @@ public class MOAT {
         check(! m.containsKey(1));
     }
 
+    private static void testImmutableMapEntry(final Map.Entry<Integer,Integer> me) {
+        Integer key = me.getKey();
+        Integer val = me.getValue();
+        THROWS(UnsupportedOperationException.class,
+               () -> me.setValue(3));
+        equal(key, me.getKey());
+        equal(val, me.getValue());
+    }
+
     private static void testImmutableMap(final Map<Integer,Integer> m) {
         THROWS(UnsupportedOperationException.class,
                () -> m.put(1,1),
@@ -615,18 +651,39 @@ public class MOAT {
             THROWS(UnsupportedOperationException.class,
                    () -> m.remove(first),
                    () -> m.clear());
-            final Map.Entry<Integer,Integer> me
-                = m.entrySet().iterator().next();
-            Integer key = me.getKey();
-            Integer val = me.getValue();
-            THROWS(UnsupportedOperationException.class,
-                   () -> me.setValue(3));
-            equal(key, me.getKey());
-            equal(val, me.getValue());
+            testImmutableMapEntry(m.entrySet().iterator().next());
         }
-        testImmutableSet(m.keySet());
-        testImmutableCollection(m.values());
-        //testImmutableSet(m.entrySet());
+        testImmutableSet(m.keySet(), 99);
+        testImmutableCollection(m.values(), 99);
+        testImmutableSet(m.entrySet(), Map.entry(42, 43));
+    }
+
+    private static void testImmutableSeqMap(final SequencedMap<Integer,Integer> m) {
+        SequencedMap<Integer,Integer> r = m.reversed();
+        testImmutableMap(m);
+        testImmutableMap(r);
+        THROWS(UnsupportedOperationException.class,
+               () -> m.putFirst(0, 0),
+               () -> m.putLast(0, 0),
+               () -> r.putFirst(0, 0),
+               () -> r.putLast(0, 0));
+        if (! m.isEmpty()) {
+            THROWS(UnsupportedOperationException.class,
+                   () -> m.pollFirstEntry(),
+                   () -> m.pollLastEntry(),
+                   () -> r.pollFirstEntry(),
+                   () -> r.pollLastEntry());
+            testImmutableMapEntry(m.sequencedEntrySet().getFirst());
+            testImmutableMapEntry(r.sequencedEntrySet().getFirst());
+            testImmutableMapEntry(m.sequencedEntrySet().reversed().getFirst());
+            testImmutableMapEntry(r.sequencedEntrySet().reversed().getFirst());
+        }
+        testImmutableSeqSet(m.sequencedKeySet(), 99);
+        testImmutableSeqColl(m.sequencedValues(), 99);
+        testImmutableSeqSet(m.sequencedEntrySet(), Map.entry(42, 43));
+        testImmutableSeqSet(r.sequencedKeySet(), 99);
+        testImmutableSeqColl(r.sequencedValues(), 99);
+        testImmutableSeqSet(r.sequencedEntrySet(), Map.entry(42, 43));
     }
 
     private static void clear(Map<?,?> m) {
diff --git a/test/jdk/java/util/Collections/Wrappers.java b/test/jdk/java/util/Collections/Wrappers.java
index 3882a4efdd1..22a96b74623 100644
--- a/test/jdk/java/util/Collections/Wrappers.java
+++ b/test/jdk/java/util/Collections/Wrappers.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 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
@@ -33,14 +33,13 @@ import java.util.Collection;
 import java.util.Collections;
 import java.util.LinkedList;
 import java.util.List;
-import java.util.Objects;
 import java.util.TreeMap;
 import java.util.TreeSet;
 
 import org.testng.annotations.Test;
 import org.testng.annotations.DataProvider;
 
-import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.assertTrue;
 
 @Test(groups = "unit")
 public class Wrappers {
@@ -66,9 +65,11 @@ public class Wrappers {
         }
 
         cases.add(new Object[] { Collections.unmodifiableCollection(seedList) });
+        cases.add(new Object[] { Collections.unmodifiableSequencedCollection(seedList) });
         cases.add(new Object[] { Collections.unmodifiableList(seedList) });
         cases.add(new Object[] { Collections.unmodifiableList(seedRandomAccess) });
         cases.add(new Object[] { Collections.unmodifiableSet(seedSet) });
+        cases.add(new Object[] { Collections.unmodifiableSequencedSet(seedSet) });
         cases.add(new Object[] { Collections.unmodifiableSortedSet(seedSet) });
         cases.add(new Object[] { Collections.unmodifiableNavigableSet(seedSet) });
 
@@ -77,6 +78,24 @@ public class Wrappers {
         cases.add(new Object[] { Collections.unmodifiableMap(seedMap).entrySet() });
         cases.add(new Object[] { Collections.unmodifiableMap(seedMap).keySet() });
         cases.add(new Object[] { Collections.unmodifiableMap(seedMap).values() });
+        cases.add(new Object[] { Collections.unmodifiableSequencedMap(seedMap).entrySet() });
+        cases.add(new Object[] { Collections.unmodifiableSequencedMap(seedMap).keySet() });
+        cases.add(new Object[] { Collections.unmodifiableSequencedMap(seedMap).values() });
+        cases.add(new Object[] { Collections.unmodifiableSequencedMap(seedMap).reversed().entrySet() });
+        cases.add(new Object[] { Collections.unmodifiableSequencedMap(seedMap).reversed().keySet() });
+        cases.add(new Object[] { Collections.unmodifiableSequencedMap(seedMap).reversed().values() });
+        cases.add(new Object[] { Collections.unmodifiableSequencedMap(seedMap).sequencedEntrySet() });
+        cases.add(new Object[] { Collections.unmodifiableSequencedMap(seedMap).sequencedKeySet() });
+        cases.add(new Object[] { Collections.unmodifiableSequencedMap(seedMap).sequencedValues() });
+        cases.add(new Object[] { Collections.unmodifiableSequencedMap(seedMap).sequencedEntrySet().reversed() });
+        cases.add(new Object[] { Collections.unmodifiableSequencedMap(seedMap).sequencedKeySet().reversed() });
+        cases.add(new Object[] { Collections.unmodifiableSequencedMap(seedMap).sequencedValues().reversed() });
+        cases.add(new Object[] { Collections.unmodifiableSequencedMap(seedMap).reversed().sequencedEntrySet() });
+        cases.add(new Object[] { Collections.unmodifiableSequencedMap(seedMap).reversed().sequencedKeySet() });
+        cases.add(new Object[] { Collections.unmodifiableSequencedMap(seedMap).reversed().sequencedValues() });
+        cases.add(new Object[] { Collections.unmodifiableSequencedMap(seedMap).reversed().sequencedEntrySet().reversed() });
+        cases.add(new Object[] { Collections.unmodifiableSequencedMap(seedMap).reversed().sequencedKeySet().reversed() });
+        cases.add(new Object[] { Collections.unmodifiableSequencedMap(seedMap).reversed().sequencedValues().reversed() });
         cases.add(new Object[] { Collections.unmodifiableSortedMap(seedMap).entrySet() });
         cases.add(new Object[] { Collections.unmodifiableSortedMap(seedMap).keySet() });
         cases.add(new Object[] { Collections.unmodifiableSortedMap(seedMap).values() });
@@ -136,11 +155,14 @@ public class Wrappers {
     @Test(dataProvider = "collections")
     public static void testAllDefaultMethodsOverridden(Collection c) throws NoSuchMethodException {
         Class cls = c.getClass();
+        var notOverridden = new ArrayList<Method>();
         for (Method m: defaultMethods) {
             Method m2 = cls.getMethod(m.getName(), m.getParameterTypes());
-            // default had been override
-            assertFalse(m2.isDefault(), cls.getCanonicalName());
+            if (m2.isDefault()) {
+                notOverridden.add(m);
+            }
         }
+        assertTrue(notOverridden.isEmpty(), cls.getName() + " does not override " + notOverridden);
     }
 }
 
diff --git a/test/jdk/java/util/SequencedCollection/Basic.java b/test/jdk/java/util/SequencedCollection/Basic.java
new file mode 100644
index 00000000000..32a5d4e5c91
--- /dev/null
+++ b/test/jdk/java/util/SequencedCollection/Basic.java
@@ -0,0 +1,857 @@
+/*
+ * Copyright (c) 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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.io.*;
+import java.util.*;
+import java.util.concurrent.CopyOnWriteArrayList;
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertSame;
+import static org.testng.Assert.assertThrows;
+import static org.testng.Assert.assertTrue;
+
+/*
+ * @test
+ * @bug     8266571
+ * @summary Basic tests for SequencedCollection
+ * @modules java.base/java.util:open
+ * @build   SimpleDeque SimpleList SimpleSortedSet
+ * @run     testng Basic
+ */
+
+// TODO test that remove(obj) with duplicates removes the right element
+
+public class Basic {
+
+    // ========== Data Providers ==========
+
+    static final List<String> ORIGINAL = List.of("a", "b", "c", "d", "e", "f", "g");
+
+    static List<String> cklist(List<String> contents) {
+        return Collections.checkedList(contents, String.class);
+    }
+
+    static NavigableSet<String> cknav(NavigableSet<String> set) {
+        return Collections.checkedNavigableSet(set, String.class);
+    }
+
+    static SortedSet<String> cksorted(SortedSet<String> set) {
+        return Collections.checkedSortedSet(set, String.class);
+    }
+
+    static SequencedSet<String> setFromMap(List<String> contents) {
+        var lhm = new LinkedHashMap<String, Boolean>();
+        var ss = Collections.newSequencedSetFromMap(lhm);
+        ss.addAll(contents);
+        return ss;
+    }
+
+    static SequencedCollection<String> ucoll(SequencedCollection<String> coll) {
+        return Collections.unmodifiableSequencedCollection(coll);
+    }
+
+    static SequencedCollection<String> ulist(List<String> list) {
+        return Collections.unmodifiableList(list);
+    }
+
+    static NavigableSet<String> unav(NavigableSet<String> set) {
+        return Collections.unmodifiableNavigableSet(set);
+    }
+
+    static SequencedSet<String> uset(SequencedSet<String> set) {
+        return Collections.unmodifiableSequencedSet(set);
+    }
+
+    static SortedSet<String> usorted(SortedSet<String> set) {
+        return Collections.unmodifiableSortedSet(set);
+    }
+
+    static <T> List<T> copyReversed(List<T> list) {
+        var r = new ArrayList<T>(list);
+        Collections.reverse(r);
+        return r;
+    }
+
+    @DataProvider(name="all")
+    public Iterator<Object[]> all() {
+        var result = new ArrayList<Object[]>();
+        populated().forEachRemaining(result::add);
+        empties().forEachRemaining(result::add);
+        return result.iterator();
+    }
+
+    @DataProvider(name="populated")
+    public Iterator<Object[]> populated() {
+        return Arrays.asList(
+            new Object[] { "ArrayDeque", new ArrayDeque<>(ORIGINAL), ORIGINAL },
+            new Object[] { "ArrayList", new ArrayList<>(ORIGINAL), ORIGINAL },
+            new Object[] { "AsList", Arrays.asList(ORIGINAL.toArray()), ORIGINAL },
+            new Object[] { "COWAL", new CopyOnWriteArrayList<>(ORIGINAL), ORIGINAL },
+            new Object[] { "LinkedHashSet", new LinkedHashSet<>(ORIGINAL), ORIGINAL },
+            new Object[] { "LinkedList", new LinkedList<>(ORIGINAL), ORIGINAL },
+            new Object[] { "ListOf", ORIGINAL, ORIGINAL },
+            new Object[] { "SetFromMap", setFromMap(ORIGINAL), ORIGINAL },
+            new Object[] { "SimpleDeque", new SimpleDeque<>(ORIGINAL), ORIGINAL },
+            new Object[] { "SimpleList", new SimpleList<>(ORIGINAL), ORIGINAL },
+            new Object[] { "SimpleSortedSet", new SimpleSortedSet<>(ORIGINAL), ORIGINAL },
+            new Object[] { "TreeSet", new TreeSet<>(ORIGINAL), ORIGINAL },
+            new Object[] { "UnmodColl", ucoll(new ArrayList<>(ORIGINAL)), ORIGINAL },
+            new Object[] { "UnmodSet", uset(new LinkedHashSet<>(ORIGINAL)), ORIGINAL }
+        ).iterator();
+    }
+
+    @DataProvider(name="empties")
+    public Iterator<Object[]> empties() {
+        return Arrays.asList(
+            new Object[] { "ArrayDeque", new ArrayDeque<>(), List.of() },
+            new Object[] { "ArrayList", new ArrayList<>(), List.of() },
+            new Object[] { "AsList", Arrays.asList(new String[0]), List.of() },
+            new Object[] { "COWAL", new CopyOnWriteArrayList<>(), List.of() },
+            new Object[] { "EmptyList", Collections.emptyList(), List.of() },
+            new Object[] { "EmptyNavigableSet", Collections.emptyNavigableSet(), List.of() },
+            new Object[] { "EmptySortedSet", Collections.emptySortedSet(), List.of() },
+            new Object[] { "LinkedHashSet", new LinkedHashSet<>(), List.of() },
+            new Object[] { "LinkedList", new LinkedList<>(), List.of() },
+            new Object[] { "ListOf", List.of(), List.of() },
+            new Object[] { "SetFromMap", setFromMap(List.of()), List.of() },
+            new Object[] { "SimpleDeque", new SimpleDeque<>(), List.of() },
+            new Object[] { "SimpleList", new SimpleList<>(), List.of() },
+            new Object[] { "SimpleSortedSet", new SimpleSortedSet<>(), List.of() },
+            new Object[] { "TreeSet", new TreeSet<>(), List.of() },
+            new Object[] { "UnmodColl", ucoll(new ArrayList<>()), List.of() },
+            new Object[] { "UnmodSet", uset(new LinkedHashSet<>()), List.of() }
+        ).iterator();
+    }
+
+    @DataProvider(name="adds")
+    public Iterator<Object[]> adds() {
+        return Arrays.asList(
+            new Object[] { "ArrayDeque", new ArrayDeque<>(ORIGINAL), ORIGINAL },
+            new Object[] { "ArrayList", new ArrayList<>(ORIGINAL), ORIGINAL },
+            new Object[] { "COWAL", new CopyOnWriteArrayList<>(ORIGINAL), ORIGINAL },
+            new Object[] { "LinkedHashSet", new LinkedHashSet<>(ORIGINAL), ORIGINAL },
+            new Object[] { "LinkedList", new LinkedList<>(ORIGINAL), ORIGINAL },
+            new Object[] { "SetFromMap", setFromMap(ORIGINAL), ORIGINAL },
+            new Object[] { "SimpleDeque", new SimpleDeque<>(ORIGINAL), ORIGINAL },
+            new Object[] { "SimpleList", new SimpleList<>(ORIGINAL), ORIGINAL }
+        ).iterator();
+    }
+
+    @DataProvider(name="unpositionedAdd")
+    public Iterator<Object[]> unpositionedAdd() {
+        return Arrays.<Object[]>asList(
+            new Object[] { "LinkedHashSet", new LinkedHashSet<>(ORIGINAL), ORIGINAL }
+        ).iterator();
+    }
+
+    @DataProvider(name="removes")
+    public Iterator<Object[]> removes() {
+        return Arrays.asList(
+            new Object[] { "ArrayDeque", new ArrayDeque<>(ORIGINAL), ORIGINAL },
+            new Object[] { "ArrayList", new ArrayList<>(ORIGINAL), ORIGINAL },
+            new Object[] { "COWAL", new CopyOnWriteArrayList<>(ORIGINAL), ORIGINAL },
+            new Object[] { "LinkedHashSet", new LinkedHashSet<>(ORIGINAL), ORIGINAL },
+            new Object[] { "LinkedList", new LinkedList<>(ORIGINAL), ORIGINAL },
+            new Object[] { "SetFromMap", setFromMap(ORIGINAL), ORIGINAL },
+            new Object[] { "SimpleDeque", new SimpleDeque<>(ORIGINAL), ORIGINAL },
+            new Object[] { "SimpleList", new SimpleList<>(ORIGINAL), ORIGINAL },
+            new Object[] { "SimpleSortedSet", new SimpleSortedSet<>(ORIGINAL), ORIGINAL },
+            new Object[] { "TreeSet", new TreeSet<>(ORIGINAL), ORIGINAL }
+        ).iterator();
+    }
+
+    @DataProvider(name="emptyRemoves")
+    public Iterator<Object[]> emptyRemoves() {
+        return Arrays.asList(
+            new Object[] { "ArrayDeque", new ArrayDeque<>(), List.of() },
+            new Object[] { "ArrayList", new ArrayList<>(), List.of() },
+            new Object[] { "COWAL", new CopyOnWriteArrayList<>(), List.of() },
+            new Object[] { "LinkedHashSet", new LinkedHashSet<>(), List.of() },
+            new Object[] { "LinkedList", new LinkedList<>(), List.of() },
+            new Object[] { "SetFromMap", setFromMap(List.of()), List.of() },
+            new Object[] { "SimpleDeque", new SimpleDeque<>(), List.of() },
+            new Object[] { "SimpleList", new SimpleList<>(), List.of() },
+            new Object[] { "SimpleSortedSet", new SimpleSortedSet<>(), List.of() },
+            new Object[] { "TreeSet", new TreeSet<>(), List.of() }
+        ).iterator();
+    }
+
+    @DataProvider(name="serializable")
+    public Iterator<Object[]> serializable() {
+        return Arrays.asList(
+            new Object[] { "ArrayDeque", new ArrayDeque<>(ORIGINAL), ORIGINAL },
+            new Object[] { "ArrayList", new ArrayList<>(ORIGINAL), ORIGINAL },
+            new Object[] { "AsList", Arrays.asList(ORIGINAL.toArray()), ORIGINAL },
+            new Object[] { "COWAL", new CopyOnWriteArrayList<>(ORIGINAL), ORIGINAL },
+            new Object[] { "LinkedHashSet", new LinkedHashSet<>(ORIGINAL), ORIGINAL },
+            new Object[] { "LinkedList", new LinkedList<>(ORIGINAL), ORIGINAL },
+            new Object[] { "ListOf", ORIGINAL, ORIGINAL },
+            new Object[] { "SetFromMap", setFromMap(ORIGINAL), ORIGINAL },
+            new Object[] { "TreeSet", new TreeSet<>(ORIGINAL), ORIGINAL },
+            new Object[] { "UnmodColl", ucoll(new ArrayList<>(ORIGINAL)), ORIGINAL },
+            new Object[] { "UnmodSet", uset(new LinkedHashSet<>(ORIGINAL)), ORIGINAL }
+        ).iterator();
+    }
+
+    @DataProvider(name="notSerializable")
+    public Iterator<Object[]> notSerializable() {
+        return Arrays.asList(
+            new Object[] { "ArrayDeque", new ArrayDeque<>(ORIGINAL).reversed() },
+            new Object[] { "ArrayList", new ArrayList<>(ORIGINAL).reversed() },
+            new Object[] { "AsList", Arrays.asList(ORIGINAL.toArray()).reversed() },
+            new Object[] { "COWAL", new CopyOnWriteArrayList<>(ORIGINAL).reversed() },
+            new Object[] { "LinkedHashSet", new LinkedHashSet<>(ORIGINAL).reversed() },
+            new Object[] { "LinkedList", new LinkedList<>(ORIGINAL).reversed() },
+            new Object[] { "ListOf", ORIGINAL.reversed() },
+            new Object[] { "SetFromMap", setFromMap(ORIGINAL).reversed() },
+            new Object[] { "UnmodColl", ucoll(new ArrayList<>(ORIGINAL)).reversed() },
+            new Object[] { "UnmodSet", uset(new LinkedHashSet<>(ORIGINAL)).reversed() }
+        ).iterator();
+    }
+
+    @DataProvider(name="doubleReverse")
+    public Iterator<Object[]> doubleReverse() {
+        return Arrays.asList(
+            new Object[] { "ArrayDeque", new ArrayDeque<>(ORIGINAL) },
+            new Object[] { "ArrayList", new ArrayList<>(ORIGINAL) },
+            new Object[] { "AsList", Arrays.asList(ORIGINAL.toArray()) },
+            new Object[] { "COWAL", new CopyOnWriteArrayList<>(ORIGINAL) },
+            new Object[] { "LinkedHashSet", new LinkedHashSet<>(ORIGINAL) },
+            new Object[] { "LinkedList", new LinkedList<>(ORIGINAL) },
+            new Object[] { "ListOf", ORIGINAL },
+            new Object[] { "SimpleDeque", new SimpleDeque<>(ORIGINAL) },
+            new Object[] { "SimpleList", new SimpleList<>(ORIGINAL) },
+            new Object[] { "SimpleSortedSet", new SimpleSortedSet<>(ORIGINAL) }
+        ).iterator();
+    }
+
+    @DataProvider(name="unmodifiable")
+    public Iterator<Object[]> unmodifiable() {
+        return Arrays.asList(
+            new Object[] { "ListOf", ORIGINAL, ORIGINAL },
+            new Object[] { "ListOfSub", ORIGINAL.subList(1, 3), ORIGINAL.subList(1, 3) },
+            new Object[] { "SingleList", Collections.singletonList("a"), List.of("a") },
+            new Object[] { "UnmodColl", ucoll(new ArrayList<>(ORIGINAL)), ORIGINAL },
+            new Object[] { "UnmodList", ulist(new ArrayList<>(ORIGINAL)), ORIGINAL },
+            new Object[] { "UnmodNav", unav(new TreeSet<>(ORIGINAL)), ORIGINAL },
+            new Object[] { "UnmodSet", uset(new LinkedHashSet<>(ORIGINAL)), ORIGINAL },
+            new Object[] { "UnmodSorted", usorted(new TreeSet<>(ORIGINAL)), ORIGINAL }
+        ).iterator();
+    }
+
+    @DataProvider(name="checkedList")
+    public Iterator<Object[]> checkedList() {
+        return Arrays.<Object[]>asList(
+            new Object[] { "ChkList", cklist(new ArrayList<>(ORIGINAL)), ORIGINAL }
+        ).iterator();
+    }
+
+    @DataProvider(name="checkedNavSet")
+    public Iterator<Object[]> checkedNavSet() {
+        return Arrays.<Object[]>asList(
+            new Object[] { "ChkNav", cknav(new TreeSet<>(ORIGINAL)), ORIGINAL }
+        ).iterator();
+    }
+
+    @DataProvider(name="checkedSortedSet")
+    public Iterator<Object[]> checkedSortedSet() {
+        return Arrays.<Object[]>asList(
+            new Object[] { "ChkSorted", cksorted(new TreeSet<>(ORIGINAL)), ORIGINAL }
+        ).iterator();
+    }
+
+    // mode bit tests for subList testing
+
+    boolean reverseList(int mode) { return (mode & 1) != 0; }
+    boolean reverseSub(int mode)  { return (mode & 2) != 0; }
+    boolean isReversed(int mode) { return reverseList(mode) ^ reverseSub(mode); }
+
+    List<String> applyMode(int mode, List<String> base) {
+        var list = reverseList(mode) ? base.reversed() : base;
+        var sub = list.subList(2, 5);
+        return reverseSub(mode) ? sub.reversed() : sub;
+    }
+
+    /**
+     * Generate cases for testing subLists. For each different List implementation, generate 4
+     * cases from the two bits of the testing mode int value:
+     *
+     *  (bit 1) if true, the List is reversed
+     *  (bit 2) if true, the subList is reversed
+     *
+     * @return the generated cases
+     */
+    @DataProvider(name="subListMods")
+    public Iterator<Object[]> subListMods() {
+        var cases = new ArrayList<Object[]>();
+        for (int mode = 0; mode < 4; mode++) {
+            cases.addAll(Arrays.asList(
+                new Object[] { "ArrayList", mode, new ArrayList<>(ORIGINAL), ORIGINAL },
+                new Object[] { "COWAL", mode, new CopyOnWriteArrayList<>(ORIGINAL), ORIGINAL },
+                new Object[] { "LinkedList", mode, new LinkedList<>(ORIGINAL), ORIGINAL },
+                new Object[] { "SimpleList", mode, new SimpleList<>(ORIGINAL), ORIGINAL }
+            ));
+        }
+        return cases.iterator();
+    }
+
+    @DataProvider(name="iteratorMods")
+    public Iterator<Object[]> iteratorMods() {
+        var cases = new ArrayList<Object[]>();
+        for (boolean rev : List.of(false, true)) {
+            cases.addAll(Arrays.asList(
+                new Object[] { "ArrayList", rev, new ArrayList<>(ORIGINAL), ORIGINAL },
+                new Object[] { "LinkedList", rev, new LinkedList<>(ORIGINAL), ORIGINAL },
+                new Object[] { "SimpleList", rev, new SimpleList<>(ORIGINAL), ORIGINAL }
+            ));
+        }
+        return cases.iterator();
+    }
+
+    @DataProvider(name="subListIteratorMods")
+    public Iterator<Object[]> subListIteratorMods() {
+        var cases = new ArrayList<Object[]>();
+        for (int mode = 0; mode < 4; mode++) {
+            cases.addAll(Arrays.asList(
+                new Object[] { "ArrayList", mode, new ArrayList<>(ORIGINAL), ORIGINAL },
+                new Object[] { "LinkedList", mode, new LinkedList<>(ORIGINAL), ORIGINAL },
+                new Object[] { "SimpleList", mode, new SimpleList<>(ORIGINAL), ORIGINAL }
+            ));
+        }
+        return cases.iterator();
+    }
+
+    // ========== Assertions ==========
+
+    /**
+     * Basic checks over the contents of a SequencedCollection,
+     * compared to a reference List, in one direction.
+     *
+     * @param seq the SequencedCollection under test
+     * @param ref the reference List
+     */
+    public void checkContents1(SequencedCollection<String> seq, List<String> ref) {
+        var list1 = new ArrayList<String>();
+        for (var s : seq)
+            list1.add(s);
+        assertEquals(list1, ref);
+
+        var list2 = new ArrayList<String>();
+        seq.forEach(list2::add);
+        assertEquals(list2, ref);
+
+        var list3 = Arrays.asList(seq.toArray());
+        assertEquals(list3, ref);
+
+        var list4 = Arrays.asList(seq.toArray(new String[0]));
+        assertEquals(list4, ref);
+
+        var list5 = Arrays.asList(seq.toArray(String[]::new));
+        assertEquals(list5, ref);
+
+        var list6 = seq.stream().toList();
+        assertEquals(list6, ref);
+
+        var list7 = seq.parallelStream().toList();
+        assertEquals(list7, ref);
+
+        assertEquals(seq.size(), ref.size());
+        assertEquals(seq.isEmpty(), ref.isEmpty());
+
+        for (var s : ref) {
+            assertTrue(seq.contains(s));
+        }
+    }
+
+    /**
+     * Check the contents of a SequencedCollection against a reference List,
+     * in both directions.
+     *
+     * @param seq the SequencedCollection under test
+     * @param ref the reference List
+     */
+    public void checkContents(SequencedCollection<String> seq, List<String> ref) {
+        checkContents1(seq, ref);
+
+        var rref = copyReversed(ref);
+        var rseq = seq.reversed();
+        checkContents1(rseq, rref);
+
+        var rrseq = rseq.reversed();
+        checkContents1(rrseq, ref);
+    }
+
+    /**
+     * Check that modification operations will throw UnsupportedOperationException,
+     * in one direction.
+     *
+     * @param seq the SequencedCollection under test
+     */
+    public void checkUnmodifiable1(SequencedCollection<String> seq) {
+        final var UOE = UnsupportedOperationException.class;
+
+        assertThrows(UOE, () -> seq.add("x"));
+        assertThrows(UOE, () -> seq.clear());
+        assertThrows(UOE, () -> { var it = seq.iterator(); it.next(); it.remove(); });
+        assertThrows(UOE, () -> seq.removeIf(x -> true));
+
+        assertThrows(UOE, () -> seq.addFirst("x"));
+        assertThrows(UOE, () -> seq.addLast("x"));
+        assertThrows(UOE, () -> seq.removeFirst());
+        assertThrows(UOE, () -> seq.removeLast());
+
+// TODO these ops should throw unconditionally, but they don't in some implementations
+     // assertThrows(UOE, () -> seq.addAll(List.of()));
+     // assertThrows(UOE, () -> seq.remove("x"));
+     // assertThrows(UOE, () -> seq.removeAll(List.of()));
+     // assertThrows(UOE, () -> seq.removeIf(x -> false));
+     // assertThrows(UOE, () -> seq.retainAll(seq));
+        assertThrows(UOE, () -> seq.addAll(seq));
+        assertThrows(UOE, () -> seq.remove(seq.iterator().next()));
+        assertThrows(UOE, () -> seq.removeAll(seq));
+        assertThrows(UOE, () -> seq.retainAll(List.of()));
+    }
+
+    /**
+     * Check that modification operations will throw UnsupportedOperationException,
+     * in both directions.
+     *
+     * @param seq the SequencedCollection under test
+     */
+    public void checkUnmodifiable(SequencedCollection<String> seq) {
+        checkUnmodifiable1(seq);
+        checkUnmodifiable1(seq.reversed());
+    }
+
+    static final Class<? extends Throwable> CCE = ClassCastException.class;
+
+    public void checkCheckedList(List<String> list) {
+        List<Object> objList = (List<Object>)(List)list;
+        assertThrows(CCE, () -> { objList.addFirst(new Object()); });
+        assertThrows(CCE, () -> { objList.addLast(new Object()); });
+        assertThrows(CCE, () -> { objList.reversed().addFirst(new Object()); });
+        assertThrows(CCE, () -> { objList.reversed().addLast(new Object()); });
+    }
+
+    public void checkCheckedNavSet(NavigableSet<String> set) {
+        NavigableSet<Object> objSet = (NavigableSet<Object>)(NavigableSet)set;
+        assertThrows(CCE, () -> { objSet.add(new Object()); });
+        assertThrows(CCE, () -> { objSet.reversed().add(new Object()); });
+    }
+
+    public void checkCheckedSortedSet(SortedSet<String> set) {
+        SortedSet<Object> objSet = (SortedSet<Object>)(SortedSet)set;
+        assertThrows(CCE, () -> { objSet.add(new Object()); });
+        assertThrows(CCE, () -> { objSet.reversed().add(new Object()); });
+    }
+
+    // ========== Tests ==========
+
+    @Test(dataProvider="all")
+    public void testFundamentals(String label, SequencedCollection<String> seq, List<String> ref) {
+        checkContents(seq, ref);
+    }
+
+    @Test(dataProvider="populated")
+    public void testGetFirst(String label, SequencedCollection<String> seq, List<String> ref) {
+        assertEquals(seq.getFirst(), ref.get(0));
+        assertEquals(seq.reversed().getFirst(), ref.get(ref.size() - 1));
+        checkContents(seq, ref);
+    }
+
+    @Test(dataProvider="populated")
+    public void testGetLast(String label, SequencedCollection<String> seq, List<String> ref) {
+        assertEquals(seq.getLast(), ref.get(ref.size() - 1));
+        assertEquals(seq.reversed().getLast(), ref.get(0));
+        checkContents(seq, ref);
+    }
+
+    @Test(dataProvider="empties")
+    public void testEmptyGetFirst(String label, SequencedCollection<String> seq, List<String> ref) {
+        assertThrows(NoSuchElementException.class, () -> seq.getFirst());
+        assertThrows(NoSuchElementException.class, () -> seq.reversed().getFirst());
+        checkContents(seq, ref);
+    }
+
+    @Test(dataProvider="empties")
+    public void testEmptyGetLast(String label, SequencedCollection<String> seq, List<String> ref) {
+        assertThrows(NoSuchElementException.class, () -> seq.getLast());
+        assertThrows(NoSuchElementException.class, () -> seq.reversed().getLast());
+        checkContents(seq, ref);
+    }
+
+    @Test(dataProvider="adds")
+    public void testAddFirst(String label, SequencedCollection<String> seq, List<String> baseref) {
+        var ref = new ArrayList<>(baseref);
+        ref.add(0, "x");
+        seq.addFirst("x");
+        checkContents(seq, ref);
+    }
+
+    @Test(dataProvider="adds")
+    public void testAddFirstRev(String label, SequencedCollection<String> seq, List<String> baseref) {
+        var ref = new ArrayList<>(baseref);
+        ref.add("x");
+        seq.reversed().addFirst("x");
+        checkContents(seq, ref);
+    }
+
+    @Test(dataProvider="adds")
+    public void testAddLast(String label, SequencedCollection<String> seq, List<String> baseref) {
+        var ref = new ArrayList<>(baseref);
+        ref.add("x");
+        seq.addLast("x");
+        checkContents(seq, ref);
+    }
+
+    @Test(dataProvider="adds")
+    public void testAddLastRev(String label, SequencedCollection<String> seq, List<String> baseref) {
+        var ref = new ArrayList<>(baseref);
+        ref.add(0, "x");
+        seq.reversed().addLast("x");
+        checkContents(seq, ref);
+    }
+
+    @Test(dataProvider="unpositionedAdd")
+    public void testUnpositionedAdd(String label, SequencedCollection<String> seq, List<String> baseref) {
+        var ref = new ArrayList<>(baseref);
+        ref.add("x");
+        seq.add("x");
+        checkContents(seq, ref);
+    }
+
+    @Test(dataProvider="unpositionedAdd")
+    public void testUnpositionedAddRev(String label, SequencedCollection<String> seq, List<String> baseref) {
+        var ref = new ArrayList<>(baseref);
+        ref.add("x");
+        seq.reversed().add("x");
+        checkContents(seq, ref);
+    }
+
+    @Test(dataProvider="removes")
+    public void testRemoveFirst(String label, SequencedCollection<String> seq, List<String> baseref) {
+        var ref = new ArrayList<>(baseref);
+        var exp = ref.remove(0);
+        var act = seq.removeFirst();
+        assertEquals(act, exp);
+        checkContents(seq, ref);
+    }
+
+    @Test(dataProvider="removes")
+    public void testRemoveFirstRev(String label, SequencedCollection<String> seq, List<String> baseref) {
+        var ref = new ArrayList<>(baseref);
+        var exp = ref.remove(ref.size() - 1);
+        var act = seq.reversed().removeFirst();
+        assertEquals(act, exp);
+        checkContents(seq, ref);
+    }
+
+    @Test(dataProvider="removes")
+    public void testRemoveLast(String label, SequencedCollection<String> seq, List<String> baseref) {
+        var ref = new ArrayList<>(baseref);
+        var exp = ref.remove(ref.size() - 1);
+        var act = seq.removeLast();
+        assertEquals(act, exp);
+        checkContents(seq, ref);
+    }
+
+    @Test(dataProvider="removes")
+    public void testRemoveLastRev(String label, SequencedCollection<String> seq, List<String> baseref) {
+        var ref = new ArrayList<>(baseref);
+        var exp = ref.remove(0);
+        var act = seq.reversed().removeLast();
+        assertEquals(act, exp);
+        checkContents(seq, ref);
+    }
+
+    @Test(dataProvider="emptyRemoves")
+    public void testEmptyRemoveFirst(String label, SequencedCollection<String> seq, List<String> baseref) {
+        assertThrows(NoSuchElementException.class, () -> seq.removeFirst());
+        assertThrows(NoSuchElementException.class, () -> seq.reversed().removeFirst());
+        checkContents(seq, baseref);
+    }
+
+    @Test(dataProvider="emptyRemoves")
+    public void testEmptyRemoveLast(String label, SequencedCollection<String> seq, List<String> baseref) {
+        assertThrows(NoSuchElementException.class, () -> seq.removeLast());
+        assertThrows(NoSuchElementException.class, () -> seq.reversed().removeLast());
+        checkContents(seq, baseref);
+    }
+
+    @Test(dataProvider="serializable")
+    public void testSerializable(String label, SequencedCollection<String> seq, List<String> ref)
+        throws ClassNotFoundException, IOException
+    {
+        var baos = new ByteArrayOutputStream();
+        try (var oos = new ObjectOutputStream(baos)) {
+            oos.writeObject(seq);
+        }
+
+        try (var bais = new ByteArrayInputStream(baos.toByteArray());
+             var ois = new ObjectInputStream(bais)) {
+            var seq2 = (SequencedCollection<String>) ois.readObject();
+            checkContents(seq2, ref);
+        }
+    }
+
+    @Test(dataProvider="notSerializable")
+    public void testNotSerializable(String label, SequencedCollection<String> seq)
+        throws ClassNotFoundException, IOException
+    {
+        var baos = new ByteArrayOutputStream();
+        try (var oos = new ObjectOutputStream(baos)) {
+            assertThrows(ObjectStreamException.class, () -> oos.writeObject(seq));
+        }
+    }
+
+    @Test(dataProvider="doubleReverse")
+    public void testDoubleReverse(String label, SequencedCollection<String> seq) {
+        var rrseq = seq.reversed().reversed();
+        assertSame(rrseq, seq);
+    }
+
+    @Test(dataProvider="unmodifiable")
+    public void testUnmodifiable(String label, SequencedCollection<String> seq, List<String> ref) {
+        checkUnmodifiable(seq);
+        checkContents(seq, ref);
+    }
+
+    @Test(dataProvider="checkedList")
+    public void testCheckedList(String label, List<String> list, List<String> ref) {
+        checkCheckedList(list);
+        checkContents(list, ref);
+    }
+
+    @Test(dataProvider="checkedNavSet")
+    public void testCheckedNavSet(String label, NavigableSet<String> set, List<String> ref) {
+        checkCheckedNavSet(set);
+        checkContents(set, ref);
+    }
+
+    @Test(dataProvider="checkedSortedSet")
+    public void testCheckedSortedSet(String label, SortedSet<String> set, List<String> ref) {
+        checkCheckedSortedSet(set);
+        checkContents(set, ref);
+    }
+
+    // Indexes for subList modification tests:
+    // 0  1  2  3  4  5  6
+    // a, b, c, d, e, f, g
+    //       c, d, e
+
+    @Test(dataProvider="subListMods")
+    public void testSubListGet(String label, int mode, List<String> list, List<String> base) {
+        var sub = applyMode(mode, list);
+        assertEquals(sub.getFirst(), isReversed(mode) ? "e" : "c");
+        assertEquals(sub.getLast(),  isReversed(mode) ? "c" : "e");
+    }
+
+    @Test(dataProvider="subListMods")
+    public void testSubListAddFirst(String label, int mode, List<String> list, List<String> base) {
+        var refList = new ArrayList<>(base);
+        var sub = applyMode(mode, list);
+        var refSub = new ArrayList<>(sub);
+
+        refList.add(isReversed(mode) ? 5 : 2, "x");
+        sub.addFirst("x");
+        refSub.add(0, "x");
+
+        checkContents(sub, refSub);
+        checkContents(list, refList);
+    }
+
+    @Test(dataProvider="subListMods")
+    public void testSubListAddLast(String label, int mode, List<String> list, List<String> base) {
+        var refList = new ArrayList<>(base);
+        var sub = applyMode(mode, list);
+        var refSub = new ArrayList<>(sub);
+
+        refList.add(isReversed(mode) ? 2 : 5, "x");
+        sub.addLast("x");
+        refSub.add("x");
+
+        checkContents(sub, refSub);
+        checkContents(list, refList);
+    }
+
+    @Test(dataProvider="subListMods")
+    public void testSubListRemoveFirst(String label, int mode, List<String> list, List<String> base) {
+        var refList = new ArrayList<>(base);
+        var sub = applyMode(mode, list);
+        var refSub = new ArrayList<>(sub);
+
+        refList.remove(isReversed(mode) ? 4 : 2);
+        var act = sub.removeFirst();
+        var exp = refSub.remove(0);
+
+        assertEquals(act, exp);
+        checkContents(sub, refSub);
+        checkContents(list, refList);
+    }
+
+    @Test(dataProvider="subListMods")
+    public void testSubListRemoveLast(String label, int mode, List<String> list, List<String> base) {
+        var refList = new ArrayList<>(base);
+        var sub = applyMode(mode, list);
+        var refSub = new ArrayList<>(sub);
+
+        refList.remove(isReversed(mode) ? 2 : 4);
+        var act = sub.removeLast();
+        var exp = refSub.remove(refSub.size() - 1);
+
+        assertEquals(act, exp);
+        checkContents(sub, refSub);
+        checkContents(list, refList);
+    }
+
+    @Test(dataProvider="subListMods")
+    public void testSubListAddAllFirst(String label, int mode, List<String> list, List<String> base) {
+        var refList = new ArrayList<>(base);
+        var sub = applyMode(mode, list);
+        var refSub = new ArrayList<>(sub);
+
+        if (isReversed(mode))
+            refList.addAll(5, List.of("y", "x"));
+        else
+            refList.addAll(2, List.of("x", "y"));
+        sub.addAll(0, List.of("x", "y"));
+        refSub.addAll(0, List.of("x", "y"));
+
+        checkContents(sub, refSub);
+        checkContents(list, refList);
+    }
+
+    @Test(dataProvider="subListMods")
+    public void testSubListAddAllLast(String label, int mode, List<String> list, List<String> base) {
+        var refList = new ArrayList<>(base);
+        var sub = applyMode(mode, list);
+        var refSub = new ArrayList<>(sub);
+
+        if (isReversed(mode))
+            refList.addAll(2, List.of("y", "x"));
+        else
+            refList.addAll(5, List.of("x", "y"));
+        sub.addAll(List.of("x", "y"));
+        refSub.addAll(List.of("x", "y"));
+
+        checkContents(sub, refSub);
+        checkContents(list, refList);
+    }
+
+    @Test(dataProvider="iteratorMods")
+    public void testListIteratorAdd(String label, boolean rev, List<String> list, List<String> base) {
+        var ref = new ArrayList<>(base);
+        var it = (rev ? list.reversed() : list).listIterator();
+
+        ref.add(rev ? 5 : 2, "x");
+        it.next();
+        it.next();
+        it.add("x");
+
+        assertEquals(it.next(), rev ? "e" : "c");
+        checkContents(list, ref);
+    }
+
+    @Test(dataProvider="iteratorMods")
+    public void testListIteratorSet(String label, boolean rev, List<String> list, List<String> base) {
+        var ref = new ArrayList<>(base);
+        var it = (rev ? list.reversed() : list).listIterator();
+
+        ref.set(rev ? 5 : 1, "x");
+        it.next();
+        it.next();
+        it.set("x");
+
+        assertEquals(it.next(), rev ? "e" : "c");
+        checkContents(list, ref);
+    }
+
+    @Test(dataProvider="iteratorMods")
+    public void testListIteratorRemove(String label, boolean rev, List<String> list, List<String> base) {
+        var ref = new ArrayList<>(base);
+        var it = (rev ? list.reversed() : list).listIterator();
+
+        ref.remove(rev ? 5 : 1);
+        it.next();
+        it.next();
+        it.remove();
+
+        assertEquals(it.next(), rev ? "e" : "c");
+        checkContents(list, ref);
+    }
+
+    // SubList ListIterator modification tests.
+
+    @Test(dataProvider="subListIteratorMods")
+    public void testSubListIteratorAdd(String label, int mode, List<String> list, List<String> base) {
+        var refList = new ArrayList<>(base);
+        var sub = applyMode(mode, list);
+        var refSub = new ArrayList<>(sub);
+
+        var it = sub.listIterator();
+        it.next();
+        it.add("x");
+        refList.add(isReversed(mode) ? 4 : 3, "x");
+        refSub.add(1, "x");
+
+        assertEquals(it.next(), "d");
+        checkContents(sub, refSub);
+        checkContents(list, refList);
+    }
+
+    @Test(dataProvider="subListIteratorMods")
+    public void testSubListIteratorSet(String label, int mode, List<String> list, List<String> base) {
+        var refList = new ArrayList<>(base);
+        var sub = applyMode(mode, list);
+        var refSub = new ArrayList<>(sub);
+
+        var it = sub.listIterator();
+        it.next();
+        it.set("x");
+        refList.set(isReversed(mode) ? 4 : 2, "x");
+        refSub.set(0, "x");
+
+        assertEquals(it.next(), "d");
+        checkContents(sub, refSub);
+        checkContents(list, refList);
+    }
+
+    @Test(dataProvider="subListIteratorMods")
+    public void testSubListIteratorRemove(String label, int mode, List<String> list, List<String> base) {
+        var refList = new ArrayList<>(base);
+        var sub = applyMode(mode, list);
+        var refSub = new ArrayList<>(sub);
+
+        var it = sub.listIterator();
+        it.next();
+        it.remove();
+        refList.remove(isReversed(mode) ? 4 : 2);
+        refSub.remove(0);
+
+        assertEquals(it.next(), "d");
+        checkContents(sub, refSub);
+        checkContents(list, refList);
+    }
+}
diff --git a/test/jdk/java/util/SequencedCollection/BasicMap.java b/test/jdk/java/util/SequencedCollection/BasicMap.java
new file mode 100644
index 00000000000..96a18cc88b7
--- /dev/null
+++ b/test/jdk/java/util/SequencedCollection/BasicMap.java
@@ -0,0 +1,892 @@
+/*
+ * Copyright (c) 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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.io.*;
+import java.util.*;
+
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNull;
+import static org.testng.Assert.assertThrows;
+import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.assertSame;
+import static org.testng.Assert.assertTrue;
+
+/*
+ * @test
+ * @bug     8266571
+ * @summary Basic tests for SequencedMap
+ * @modules java.base/java.util:open
+ * @build   SimpleSortedMap
+ * @run     testng BasicMap
+ */
+
+public class BasicMap {
+
+    // ========== Data Providers ==========
+
+    static final Class<? extends Throwable> CCE  = ClassCastException.class;
+    static final Class<? extends Throwable> NSEE = NoSuchElementException.class;
+    static final Class<? extends Throwable> UOE  = UnsupportedOperationException.class;
+
+    static final List<Map.Entry<String, Integer>> ORIGINAL =
+        List.of(Map.entry("a", 1),
+                Map.entry("b", 2),
+                Map.entry("c", 3),
+                Map.entry("d", 4),
+                Map.entry("e", 5));
+
+    static <M extends SequencedMap<String, Integer>>
+    M load(M map, List<Map.Entry<String, Integer>> mappings) {
+        for (var e : mappings)
+            map.put(e.getKey(), e.getValue());
+        return map;
+    }
+
+    static NavigableMap<String, Integer> cknav(NavigableMap<String, Integer> map) {
+        return Collections.checkedNavigableMap(map, String.class, Integer.class);
+    }
+
+    static SortedMap<String, Integer> cksorted(SortedMap<String, Integer> map) {
+        return Collections.checkedSortedMap(map, String.class, Integer.class);
+    }
+
+    static SequencedMap<String, Integer> umap(SequencedMap<String, Integer> map) {
+        return Collections.unmodifiableSequencedMap(map);
+    }
+
+    static SortedMap<String, Integer> usorted(SortedMap<String, Integer> map) {
+        return Collections.unmodifiableSortedMap(map);
+    }
+
+    static NavigableMap<String, Integer> unav(NavigableMap<String, Integer> map) {
+        return Collections.unmodifiableNavigableMap(map);
+    }
+
+    @DataProvider(name="all")
+    public Iterator<Object[]> all() {
+        var result = new ArrayList<Object[]>();
+        populated().forEachRemaining(result::add);
+        empties().forEachRemaining(result::add);
+        return result.iterator();
+    }
+
+    @DataProvider(name="populated")
+    public Iterator<Object[]> populated() {
+        return Arrays.asList(
+            new Object[] { "LinkedHashMap", load(new LinkedHashMap<>(), ORIGINAL), ORIGINAL },
+            new Object[] { "SimpleSortedMap", load(new SimpleSortedMap<>(), ORIGINAL), ORIGINAL },
+            new Object[] { "TreeMap", load(new TreeMap<>(), ORIGINAL), ORIGINAL },
+            new Object[] { "UnmodMap", umap(load(new LinkedHashMap<>(), ORIGINAL)), ORIGINAL }
+        ).iterator();
+    }
+
+    @DataProvider(name="empties")
+    public Iterator<Object[]> empties() {
+        return Arrays.asList(
+            new Object[] { "EmptyNavigableMap", Collections.emptyNavigableMap(), List.of() },
+            new Object[] { "EmptySortedMap", Collections.emptySortedMap(), List.of() },
+            new Object[] { "LinkedHashMap", new LinkedHashMap<>(), List.of() },
+            new Object[] { "SimpleSortedMap", new SimpleSortedMap<>(), List.of() },
+            new Object[] { "TreeMap", new TreeMap<>(), List.of() },
+            new Object[] { "UnmodMap", umap(new LinkedHashMap<>()), List.of() }
+        ).iterator();
+    }
+
+    @DataProvider(name="polls")
+    public Iterator<Object[]> polls() {
+        return Arrays.asList(
+            new Object[] { "LinkedHashMap", load(new LinkedHashMap<>(), ORIGINAL), ORIGINAL },
+            new Object[] { "SimpleSortedMap", load(new SimpleSortedMap<>(), ORIGINAL), ORIGINAL },
+            new Object[] { "TreeMap", load(new TreeMap<>(), ORIGINAL), ORIGINAL }
+        ).iterator();
+    }
+
+    @DataProvider(name="emptyPolls")
+    public Iterator<Object[]> emptyPolls() {
+        return Arrays.asList(
+            new Object[] { "LinkedHashMap", new LinkedHashMap<>(), List.of() },
+            new Object[] { "SimpleSortedMap", new SimpleSortedMap<>(), List.of() },
+            new Object[] { "TreeMap", new TreeMap<>(), List.of() }
+        ).iterator();
+    }
+
+    @DataProvider(name="puts")
+    public Iterator<Object[]> puts() {
+        return Arrays.<Object[]>asList(
+            new Object[] { "LinkedHashMap", load(new LinkedHashMap<>(), ORIGINAL), ORIGINAL }
+        ).iterator();
+    }
+
+    @DataProvider(name="putUnpositioned")
+    public Iterator<Object[]> putUnpositioned() {
+        return Arrays.asList(
+            new Object[] { "LinkedHashMap", false, load(new LinkedHashMap<>(), ORIGINAL), ORIGINAL },
+            new Object[] { "LinkedHashMap", true,  load(new LinkedHashMap<>(), ORIGINAL), ORIGINAL }
+        ).iterator();
+    }
+
+    @DataProvider(name="putThrows")
+    public Iterator<Object[]> putThrows() {
+        return Arrays.asList(
+            new Object[] { "SimpleSortedMap", load(new SimpleSortedMap<>(), ORIGINAL), ORIGINAL },
+            new Object[] { "TreeMap", load(new TreeMap<>(), ORIGINAL), ORIGINAL }
+        ).iterator();
+    }
+
+    @DataProvider(name="serializable")
+    public Iterator<Object[]> serializable() {
+        return Arrays.asList(
+            new Object[] { "LinkedHashMap", load(new LinkedHashMap<>(), ORIGINAL), ORIGINAL },
+            new Object[] { "TreeMap", load(new TreeMap<>(), ORIGINAL), ORIGINAL },
+            new Object[] { "UnmodMap", umap(load(new LinkedHashMap<>(), ORIGINAL)), ORIGINAL }
+        ).iterator();
+    }
+
+    @DataProvider(name="notSerializable")
+    public Iterator<Object[]> notSerializable() {
+        return Arrays.asList(
+            new Object[] { "LinkedHashMap", load(new LinkedHashMap<>(), ORIGINAL).reversed() },
+            new Object[] { "UnmodMap", umap(load(new LinkedHashMap<>(), ORIGINAL)).reversed() }
+        ).iterator();
+    }
+
+    @DataProvider(name="doubleReverse")
+    public Iterator<Object[]> doubleReverse() {
+        return Arrays.<Object[]>asList(
+            new Object[] { "LinkedHashMap", load(new LinkedHashMap<>(), ORIGINAL) }
+        ).iterator();
+    }
+
+    @DataProvider(name="unmodifiable")
+    public Iterator<Object[]> unmodifible() {
+        return Arrays.<Object[]>asList(
+            new Object[] { "UnmodMap", umap(load(new LinkedHashMap<>(), ORIGINAL)), ORIGINAL },
+            new Object[] { "UnmodNav", unav(load(new TreeMap<>(), ORIGINAL)), ORIGINAL },
+            new Object[] { "UnmodSorted", usorted(load(new TreeMap<>(), ORIGINAL)), ORIGINAL }
+        ).iterator();
+    }
+
+    @DataProvider(name="checked")
+    public Iterator<Object[]> checked() {
+        return Arrays.<Object[]>asList(
+            new Object[] { "ChkNav", cknav(load(new TreeMap<>(), ORIGINAL)), ORIGINAL },
+            new Object[] { "ChkSorted", cksorted(load(new TreeMap<>(), ORIGINAL)), ORIGINAL }
+        ).iterator();
+    }
+
+    // mode bit tests
+
+    boolean reverseMap(int mode)  { return (mode & 1) != 0; }
+    boolean reverseView(int mode) { return (mode & 2) != 0; }
+    boolean callLast(int mode)    { return (mode & 4) != 0; }
+
+    boolean refLast(int mode) { return reverseMap(mode) ^ reverseView(mode) ^ callLast(mode); }
+
+    /**
+     * Generate cases for testing the removeFirst and removeLast methods of map views. For each
+     * different map implementation, generate 8 cases from the three bits of the testing mode
+     * int value:
+     *
+     *  (bit 1) if true, the backing map is reversed
+     *  (bit 2) if true, the view is reversed
+     *  (bit 4) if true, the last element of the view is to be removed, otherwise the first
+     *
+     * The three bits XORed together (by refLast(), above) indicate (if true) the last
+     * or (if false) the first element of the reference entry list is to be removed.
+     *
+     * @return the generated cases
+     */
+    @DataProvider(name="viewRemoves")
+    public Iterator<Object[]> viewRemoves() {
+        var cases = new ArrayList<Object[]>();
+        for (int mode = 0; mode < 8; mode++) {
+            cases.addAll(Arrays.asList(
+                new Object[] { "LinkedHashMap", mode, load(new LinkedHashMap<>(), ORIGINAL), ORIGINAL },
+                new Object[] { "SimpleSortedMap", mode, load(new SimpleSortedMap<>(), ORIGINAL), ORIGINAL },
+                new Object[] { "TreeMap", mode, load(new TreeMap<>(), ORIGINAL), ORIGINAL }
+            ));
+        }
+        return cases.iterator();
+    }
+
+    @DataProvider(name="emptyViewRemoves")
+    public Iterator<Object[]> emptyViewRemoves() {
+        var cases = new ArrayList<Object[]>();
+        for (int mode = 0; mode < 8; mode++) {
+            cases.addAll(Arrays.asList(
+                new Object[] { "LinkedHashMap", mode, new LinkedHashMap<>(), List.of() },
+                new Object[] { "SimpleSortedMap", mode, new SimpleSortedMap<>(), List.of() },
+                new Object[] { "TreeMap", mode, new TreeMap<>(), List.of() }
+            ));
+        }
+        return cases.iterator();
+    }
+
+    @DataProvider(name="viewAddThrows")
+    public Iterator<Object[]> viewAddThrows() {
+        var cases = new ArrayList<Object[]>();
+        for (int mode = 0; mode < 8; mode++) {
+            cases.addAll(Arrays.asList(
+                new Object[] { "LinkedHashMap", mode, load(new LinkedHashMap<>(), ORIGINAL), ORIGINAL },
+                new Object[] { "SimpleSortedMap", mode, load(new SimpleSortedMap<>(), ORIGINAL), ORIGINAL },
+                new Object[] { "TreeMap", mode, load(new TreeMap<>(), ORIGINAL), ORIGINAL }
+            ));
+        }
+        return cases.iterator();
+    }
+
+    // ========== Assertions ==========
+
+    /**
+     * Basic checks over the contents of a SequencedMap, compared to a reference List of entries,
+     * in one direction.
+     *
+     * @param map the SequencedMap under test
+     * @param ref the reference list of entries
+     */
+    public void checkContents1(SequencedMap<String, Integer> map, List<Map.Entry<String, Integer>> ref) {
+        var list1 = new ArrayList<Map.Entry<String, Integer>>();
+        map.forEach((k, v) -> list1.add(Map.entry(k, v)));
+        assertEquals(list1, ref);
+
+        assertEquals(map.size(), ref.size());
+        assertEquals(map.isEmpty(), ref.isEmpty());
+
+        for (var e : ref) {
+            assertTrue(map.containsKey(e.getKey()));
+            assertTrue(map.containsValue(e.getValue()));
+            assertEquals(map.get(e.getKey()), e.getValue());
+        }
+    }
+
+    public void checkContents(SequencedMap<String, Integer> map, List<Map.Entry<String, Integer>> ref) {
+        checkContents1(map, ref);
+
+        var rref = new ArrayList<>(ref);
+        Collections.reverse(rref);
+        var rmap = map.reversed();
+        checkContents1(rmap, rref);
+
+        var rrmap = rmap.reversed();
+        checkContents1(rrmap, ref);
+    }
+
+    /**
+     * Check the entrySet, keySet, or values view of a SequencedMap in one direction. The view
+     * collection is ordered even though the collection type is not sequenced.
+     *
+     * @param <T> the element type of the view
+     * @param mapView the actual map view
+     * @param expElements list of the expected elements
+     */
+    public <T> void checkView1(Collection<T> mapView, List<T> expElements) {
+        var list1 = new ArrayList<T>();
+        for (var k : mapView)
+            list1.add(k);
+        assertEquals(list1, expElements);
+
+        var list2 = new ArrayList<T>();
+        mapView.forEach(list2::add);
+        assertEquals(list2, expElements);
+
+        var list3 = Arrays.asList(mapView.toArray());
+        assertEquals(list3, expElements);
+
+        var list4 = Arrays.asList(mapView.toArray(new Object[0]));
+        assertEquals(list4, expElements);
+
+        var list5 = Arrays.asList(mapView.toArray(Object[]::new));
+        assertEquals(list5, expElements);
+
+        var list6 = mapView.stream().toList();
+        assertEquals(list6, expElements);
+
+        var list7 = mapView.parallelStream().toList();
+        assertEquals(list7, expElements);
+
+        assertEquals(mapView.size(), expElements.size());
+        assertEquals(mapView.isEmpty(), expElements.isEmpty());
+
+        for (var k : expElements) {
+            assertTrue(mapView.contains(k));
+        }
+
+        var it = mapView.iterator();
+        if (expElements.isEmpty()) {
+            assertFalse(it.hasNext());
+        } else {
+            assertTrue(it.hasNext());
+            assertEquals(it.next(), expElements.get(0));
+        }
+    }
+
+    /**
+     * Check the sequenced entrySet, keySet, or values view of a SequencedMap in one direction.
+     *
+     * @param <T> the element type of the view
+     * @param mapView the actual map view
+     * @param expElements list of the expected elements
+     */
+    public <T> void checkSeqView1(SequencedCollection<T> mapView, List<T> expElements) {
+        checkView1(mapView, expElements);
+
+        if (expElements.isEmpty()) {
+            assertThrows(NoSuchElementException.class, () -> mapView.getFirst());
+            assertThrows(NoSuchElementException.class, () -> mapView.getLast());
+        } else {
+            assertEquals(mapView.getFirst(), expElements.get(0));
+            assertEquals(mapView.getLast(), expElements.get(expElements.size() - 1));
+        }
+    }
+
+    /**
+     * Check the keySet and sequencedKeySet views of a map. It's possible to unify this with
+     * the corresponding checks for values and entrySet views, but doing this adds a bunch
+     * of generics and method references that tend to obscure more than they help.
+     *
+     * @param map the SequencedMap under test
+     * @param refEntries expected contents of the map
+     */
+    public void checkKeySet(SequencedMap<String, Integer> map, List<Map.Entry<String, Integer>> refEntries) {
+        List<String> refKeys = refEntries.stream().map(Map.Entry::getKey).toList();
+        List<String> rrefKeys = new ArrayList<>(refKeys);
+        Collections.reverse(rrefKeys);
+        SequencedMap<String, Integer> rmap = map.reversed();
+
+        checkView1(map.keySet(), refKeys);
+        checkSeqView1(map.sequencedKeySet(), refKeys);
+        checkSeqView1(map.sequencedKeySet().reversed(), rrefKeys);
+
+        checkView1(rmap.keySet(), rrefKeys);
+        checkSeqView1(rmap.sequencedKeySet(), rrefKeys);
+        checkSeqView1(rmap.sequencedKeySet().reversed(), refKeys);
+
+        checkView1(rmap.reversed().keySet(), refKeys);
+        checkSeqView1(rmap.reversed().sequencedKeySet(), refKeys);
+        checkSeqView1(rmap.reversed().sequencedKeySet().reversed(), rrefKeys);
+    }
+
+    /**
+     * Check the values and sequencedValues views of a map.
+     *
+     * @param map the SequencedMap under test
+     * @param refEntries expected contents of the map
+     */
+    public void checkValues(SequencedMap<String, Integer> map, List<Map.Entry<String, Integer>> refEntries) {
+        List<Integer> refValues = refEntries.stream().map(Map.Entry::getValue).toList();
+        List<Integer> rrefValues = new ArrayList<>(refValues);
+        Collections.reverse(rrefValues);
+        SequencedMap<String, Integer> rmap = map.reversed();
+
+        checkView1(map.values(), refValues);
+        checkSeqView1(map.sequencedValues(), refValues);
+        checkSeqView1(map.sequencedValues().reversed(), rrefValues);
+
+        checkView1(rmap.values(), rrefValues);
+        checkSeqView1(rmap.sequencedValues(), rrefValues);
+        checkSeqView1(rmap.sequencedValues().reversed(), refValues);
+
+        checkView1(rmap.reversed().values(), refValues);
+        checkSeqView1(rmap.reversed().sequencedValues(), refValues);
+        checkSeqView1(rmap.reversed().sequencedValues().reversed(), rrefValues);
+    }
+
+    /**
+     * Check the entrySet and sequencedEntrySet views of a map.
+     *
+     * @param map the SequencedMap under test
+     * @param refEntries expected contents of the map
+     */
+    public void checkEntrySet(SequencedMap<String, Integer> map, List<Map.Entry<String, Integer>> refEntries) {
+        List<Map.Entry<String, Integer>> rref = new ArrayList<>(refEntries);
+        Collections.reverse(rref);
+        SequencedMap<String, Integer> rmap = map.reversed();
+
+        checkView1(map.entrySet(), refEntries);
+        checkSeqView1(map.sequencedEntrySet(), refEntries);
+        checkSeqView1(map.sequencedEntrySet().reversed(), rref);
+
+        checkView1(rmap.entrySet(), rref);
+        checkSeqView1(rmap.sequencedEntrySet(), rref);
+        checkSeqView1(rmap.sequencedEntrySet().reversed(), refEntries);
+
+        checkView1(rmap.reversed().entrySet(), refEntries);
+        checkSeqView1(rmap.reversed().sequencedEntrySet(), refEntries);
+        checkSeqView1(rmap.reversed().sequencedEntrySet().reversed(), rref);
+    }
+
+    /**
+     * Test attempted modifications to unmodifiable map views. The only mutating operation
+     * map views can support is removal.
+     *
+     * @param <T> element type of the map view
+     * @param view the map view
+     */
+    public <T> void checkUnmodifiableView(Collection<T> view) {
+        assertThrows(UOE, () -> view.clear());
+        assertThrows(UOE, () -> { var it = view.iterator(); it.next(); it.remove(); });
+        assertThrows(UOE, () -> { var t = view.iterator().next(); view.remove(t); });
+
+// TODO these ops should throw unconditionally, but they don't in some implementations
+     // assertThrows(UOE, () -> view.removeAll(List.of()));
+     // assertThrows(UOE, () -> view.removeIf(x -> false));
+     // assertThrows(UOE, () -> view.retainAll(view));
+        assertThrows(UOE, () -> view.removeAll(view));
+        assertThrows(UOE, () -> view.removeIf(x -> true));
+        assertThrows(UOE, () -> view.retainAll(List.of()));
+    }
+
+    /**
+     * Test removal methods on unmodifiable sequenced map views.
+     *
+     * @param <T> element type of the map view
+     * @param view the map view
+     */
+    public <T> void checkUnmodifiableSeqView(SequencedCollection<T> view) {
+        checkUnmodifiableView(view);
+        assertThrows(UOE, () -> view.removeFirst());
+        assertThrows(UOE, () -> view.removeLast());
+
+        var rview = view.reversed();
+        checkUnmodifiableView(rview);
+        assertThrows(UOE, () -> rview.removeFirst());
+        assertThrows(UOE, () -> rview.removeLast());
+    }
+
+    public void checkUnmodifiableEntry(SequencedMap<String, Integer> map) {
+        assertThrows(UOE, () -> { map.firstEntry().setValue(99); });
+        assertThrows(UOE, () -> { map.lastEntry().setValue(99); });
+        assertThrows(UOE, () -> { map.sequencedEntrySet().getFirst().setValue(99); });
+        assertThrows(UOE, () -> { map.sequencedEntrySet().getLast().setValue(99); });
+        assertThrows(UOE, () -> { map.sequencedEntrySet().reversed().getFirst().setValue(99); });
+        assertThrows(UOE, () -> { map.sequencedEntrySet().reversed().getLast().setValue(99); });
+    }
+
+    public void checkUnmodifiable1(SequencedMap<String, Integer> map) {
+        assertThrows(UOE, () -> map.putFirst("x", 99));
+        assertThrows(UOE, () -> map.putLast("x", 99));
+        assertThrows(UOE, () -> { map.pollFirstEntry(); });
+        assertThrows(UOE, () -> { map.pollLastEntry(); });
+
+        checkUnmodifiableEntry(map);
+        checkUnmodifiableView(map.keySet());
+        checkUnmodifiableView(map.values());
+        checkUnmodifiableView(map.entrySet());
+        checkUnmodifiableSeqView(map.sequencedKeySet());
+        checkUnmodifiableSeqView(map.sequencedValues());
+        checkUnmodifiableSeqView(map.sequencedEntrySet());
+    }
+
+    public void checkUnmodifiable(SequencedMap<String, Integer> map) {
+        checkUnmodifiable1(map);
+        checkUnmodifiable1(map.reversed());
+    }
+
+    // The putFirst/putLast operations aren't tested here, because the only instances of
+    // checked, sequenced maps are SortedMap and NavigableMap, which don't support them.
+    public void checkChecked(SequencedMap<String, Integer> map) {
+        SequencedMap<Object, Object> objMap = (SequencedMap<Object, Object>)(SequencedMap)map;
+        assertThrows(CCE, () -> { objMap.put(new Object(), 99); });
+        assertThrows(CCE, () -> { objMap.put("x", new Object()); });
+        assertThrows(CCE, () -> { objMap.sequencedEntrySet().getFirst().setValue(new Object()); });
+        assertThrows(CCE, () -> { objMap.sequencedEntrySet().reversed().getFirst().setValue(new Object()); });
+        assertThrows(CCE, () -> { objMap.reversed().put(new Object(), 99); });
+        assertThrows(CCE, () -> { objMap.reversed().put("x", new Object()); });
+        assertThrows(CCE, () -> { objMap.reversed().sequencedEntrySet().getFirst().setValue(new Object()); });
+        assertThrows(CCE, () -> { objMap.reversed().sequencedEntrySet().reversed().getFirst().setValue(new Object()); });
+    }
+
+    // ========== Tests ==========
+
+    @Test(dataProvider="all")
+    public void testFundamentals(String label, SequencedMap<String, Integer> map, List<Map.Entry<String, Integer>> ref) {
+        checkContents(map, ref);
+        checkEntrySet(map, ref);
+        checkKeySet(map, ref);
+        checkValues(map, ref);
+    }
+
+    @Test(dataProvider="populated")
+    public void testFirstEntry(String label, SequencedMap<String, Integer> map, List<Map.Entry<String, Integer>> ref) {
+        assertEquals(map.firstEntry(), ref.get(0));
+        assertEquals(map.reversed().firstEntry(), ref.get(ref.size() - 1));
+        assertThrows(UOE, () -> { map.firstEntry().setValue(99); });
+        assertThrows(UOE, () -> { map.reversed().firstEntry().setValue(99); });
+        checkContents(map, ref);
+    }
+
+    @Test(dataProvider="populated")
+    public void testLastEntry(String label, SequencedMap<String, Integer> map, List<Map.Entry<String, Integer>> ref) {
+        assertEquals(map.lastEntry(), ref.get(ref.size() - 1));
+        assertEquals(map.reversed().lastEntry(), ref.get(0));
+        assertThrows(UOE, () -> { map.lastEntry().setValue(99); });
+        assertThrows(UOE, () -> { map.reversed().lastEntry().setValue(99); });
+        checkContents(map, ref);
+    }
+
+    @Test(dataProvider="empties")
+    public void testEmptyFirstEntry(String label, SequencedMap<String, Integer> map, List<Map.Entry<String, Integer>> ref) {
+        assertNull(map.firstEntry());
+        assertNull(map.reversed().firstEntry());
+        checkContents(map, ref);
+    }
+
+    @Test(dataProvider="empties")
+    public void testEmptyLastEntry(String label, SequencedMap<String, Integer> map, List<Map.Entry<String, Integer>> ref) {
+        assertNull(map.lastEntry());
+        assertNull(map.reversed().lastEntry());
+        checkContents(map, ref);
+    }
+
+    @Test(dataProvider="puts")
+    public void testPutFirst(String label, SequencedMap<String, Integer> map, List<Map.Entry<String, Integer>> baseref) {
+        var ref = new ArrayList<>(baseref);
+        ref.add(0, Map.entry("x", 99));
+        map.putFirst("x", 99);
+        checkContents(map, ref);
+    }
+
+    @Test(dataProvider="puts")
+    public void testPutFirstRev(String label, SequencedMap<String, Integer> map, List<Map.Entry<String, Integer>> baseref) {
+        var ref = new ArrayList<>(baseref);
+        ref.add(Map.entry("x", 99));
+        map.reversed().putFirst("x", 99);
+        checkContents(map, ref);
+    }
+
+    @Test(dataProvider="puts")
+    public void testPutLast(String label, SequencedMap<String, Integer> map, List<Map.Entry<String, Integer>> baseref) {
+        var ref = new ArrayList<>(baseref);
+        ref.add(Map.entry("x", 99));
+        map.putLast("x", 99);
+        checkContents(map, ref);
+    }
+
+    @Test(dataProvider="puts")
+    public void testPutLastRev(String label, SequencedMap<String, Integer> map, List<Map.Entry<String, Integer>> baseref) {
+        var ref = new ArrayList<>(baseref);
+        ref.add(0, Map.entry("x", 99));
+        map.reversed().putLast("x", 99);
+        checkContents(map, ref);
+    }
+
+    @Test(dataProvider="putUnpositioned")
+    public void testUnposPut(String label, boolean rev, SequencedMap<String, Integer> map, List<Map.Entry<String, Integer>> baseref) {
+        var ref = new ArrayList<>(baseref);
+        ref.add(Map.entry("x", 99));
+        (rev ? map.reversed() : map).put("x", 99);
+        checkContents(map, ref);
+    }
+
+    @Test(dataProvider="putUnpositioned")
+    public void testUnposPutAll(String label, boolean rev, SequencedMap<String, Integer> map, List<Map.Entry<String, Integer>> baseref) {
+        var ref = new ArrayList<>(baseref);
+        ref.add(Map.entry("x", 99));
+        (rev ? map.reversed() : map).putAll(Map.of("x", 99));
+        checkContents(map, ref);
+    }
+
+    @Test(dataProvider="putUnpositioned")
+    public void testUnposPutIfAbsent(String label, boolean rev, SequencedMap<String, Integer> map, List<Map.Entry<String, Integer>> baseref) {
+        var ref = new ArrayList<>(baseref);
+        ref.add(Map.entry("x", 99));
+        (rev ? map.reversed() : map).putIfAbsent("x", 99);
+        checkContents(map, ref);
+    }
+
+    @Test(dataProvider="putUnpositioned")
+    public void testUnposCompute(String label, boolean rev, SequencedMap<String, Integer> map, List<Map.Entry<String, Integer>> baseref) {
+        var ref = new ArrayList<>(baseref);
+        ref.add(Map.entry("x", 99));
+        (rev ? map.reversed() : map).compute("x", (k, v) -> 99);
+        checkContents(map, ref);
+    }
+
+    @Test(dataProvider="putUnpositioned")
+    public void testUnposComputeIfAbsent(String label, boolean rev, SequencedMap<String, Integer> map, List<Map.Entry<String, Integer>> baseref) {
+        var ref = new ArrayList<>(baseref);
+        ref.add(Map.entry("x", 99));
+        (rev ? map.reversed() : map).computeIfAbsent("x", k -> 99);
+        checkContents(map, ref);
+    }
+
+    @Test(dataProvider="putUnpositioned")
+    public void testUnposMerge(String label, boolean rev, SequencedMap<String, Integer> map, List<Map.Entry<String, Integer>> baseref) {
+        var ref = new ArrayList<>(baseref);
+        ref.add(Map.entry("x", 99));
+        (rev ? map.reversed() : map).merge("x", 99, /*unused*/ (k, v) -> -1);
+        checkContents(map, ref);
+    }
+
+    @Test(dataProvider="putThrows")
+    public void testPutThrows(String label, SequencedMap<String, Integer> map, List<Map.Entry<String, Integer>> baseref) {
+        assertThrows(UOE, () -> map.putFirst("x", 99));
+        assertThrows(UOE, () -> map.putLast("x", 99));
+        assertThrows(UOE, () -> map.reversed().putFirst("x", 99));
+        assertThrows(UOE, () -> map.reversed().putLast("x", 99));
+        checkContents(map, baseref);
+    }
+
+    @Test(dataProvider="polls")
+    public void testPollFirst(String label, SequencedMap<String, Integer> map, List<Map.Entry<String, Integer>> baseref) {
+        var ref = new ArrayList<>(baseref);
+        var act = map.pollFirstEntry();
+        assertEquals(act, ref.remove(0));
+        assertThrows(UOE, () -> { act.setValue(99); });
+        checkContents(map, ref);
+    }
+
+    @Test(dataProvider="polls")
+    public void testPollFirstRev(String label, SequencedMap<String, Integer> map, List<Map.Entry<String, Integer>> baseref) {
+        var ref = new ArrayList<>(baseref);
+        var act = map.reversed().pollFirstEntry();
+        assertEquals(act, ref.remove(ref.size() - 1));
+        assertThrows(UOE, () -> { act.setValue(99); });
+        checkContents(map, ref);
+    }
+
+    @Test(dataProvider="polls")
+    public void testPollLast(String label, SequencedMap<String, Integer> map, List<Map.Entry<String, Integer>> baseref) {
+        var ref = new ArrayList<>(baseref);
+        var act = map.pollLastEntry();
+        assertEquals(act, ref.remove(ref.size() - 1));
+        assertThrows(UOE, () -> { act.setValue(99); });
+        checkContents(map, ref);
+    }
+
+    @Test(dataProvider="polls")
+    public void testPollLastRev(String label, SequencedMap<String, Integer> map, List<Map.Entry<String, Integer>> baseref) {
+        var ref = new ArrayList<>(baseref);
+        var act = map.reversed().pollLastEntry();
+        assertEquals(act, ref.remove(0));
+        assertThrows(UOE, () -> { act.setValue(99); });
+        checkContents(map, ref);
+    }
+
+    @Test(dataProvider="emptyPolls")
+    public void testEmptyPollFirst(String label, SequencedMap<String, Integer> map, List<Map.Entry<String, Integer>> ref) {
+        assertNull(map.pollFirstEntry());
+        assertNull(map.reversed().pollFirstEntry());
+        checkContents(map, ref);
+    }
+
+    @Test(dataProvider="emptyPolls")
+    public void testEmptyPollLast(String label, SequencedMap<String, Integer> map, List<Map.Entry<String, Integer>> ref) {
+        assertNull(map.pollLastEntry());
+        assertNull(map.reversed().pollLastEntry());
+        checkContents(map, ref);
+    }
+
+    @Test(dataProvider="serializable")
+    public void testSerializable(String label, SequencedMap<String, Integer> map, List<Map.Entry<String, Integer>> ref)
+        throws ClassNotFoundException, IOException
+    {
+        var baos = new ByteArrayOutputStream();
+        try (var oos = new ObjectOutputStream(baos)) {
+            oos.writeObject(map);
+        }
+
+        try (var bais = new ByteArrayInputStream(baos.toByteArray());
+             var ois = new ObjectInputStream(bais)) {
+            var map2 = (SequencedMap<String, Integer>) ois.readObject();
+            checkContents(map2, ref);
+        }
+    }
+
+    @Test(dataProvider="notSerializable")
+    public void testNotSerializable(String label, SequencedMap<String, Integer> map)
+        throws ClassNotFoundException, IOException
+    {
+        var baos = new ByteArrayOutputStream();
+        try (var oos = new ObjectOutputStream(baos)) {
+            assertThrows(ObjectStreamException.class, () -> oos.writeObject(map));
+        }
+    }
+
+    @Test(dataProvider="doubleReverse")
+    public void testDoubleReverse(String label, SequencedMap<String, Integer> map) {
+        var rrmap = map.reversed().reversed();
+        assertSame(rrmap, map);
+    }
+
+    @Test(dataProvider="unmodifiable")
+    public void testUnmodifiable(String label, SequencedMap<String, Integer> map, List<Map.Entry<String, Integer>> ref) {
+        checkUnmodifiable(map);
+        checkContents(map, ref);
+    }
+
+    @Test(dataProvider="checked")
+    public void testChecked(String label, SequencedMap<String, Integer> map, List<Map.Entry<String, Integer>> ref) {
+        checkChecked(map);
+        checkContents(map, ref);
+    }
+
+    /**
+     * Test that a removal from the sequenedKeySet view is properly reflected in the original
+     * backing map. The mode value indicates whether the backing map is reversed, whether the
+     * sequencedKeySet view is reversed, and whether the removeFirst or removeLast is called
+     * on the view. See the viewRemoves() dataProvider for details.
+     *
+     * @param label the implementation label
+     * @param mode reversed and first/last modes
+     * @param map the original map instance
+     * @param baseref reference contents of the original map
+     */
+    @Test(dataProvider="viewRemoves")
+    public void testKeySetRemoves(String label,
+                                  int mode,
+                                  SequencedMap<String, Integer> map,
+                                  List<Map.Entry<String, Integer>> baseref) {
+        var ref = new ArrayList<>(baseref);
+        var exp = (refLast(mode) ? ref.remove(ref.size() - 1) : ref.remove(0)).getKey();
+        var tempmap = reverseMap(mode) ? map.reversed() : map;
+        var keySet = reverseView(mode) ? tempmap.sequencedKeySet().reversed() : tempmap.sequencedKeySet();
+        var act = callLast(mode) ? keySet.removeLast() : keySet.removeFirst();
+        assertEquals(act, exp);
+        checkContents(map, ref);
+    }
+
+    // As above, but for the sequencedValues view.
+    @Test(dataProvider="viewRemoves")
+    public void testValuesRemoves(String label,
+                                  int mode,
+                                  SequencedMap<String, Integer> map,
+                                  List<Map.Entry<String, Integer>> baseref) {
+        var ref = new ArrayList<>(baseref);
+        var exp = (refLast(mode) ? ref.remove(ref.size() - 1) : ref.remove(0)).getValue();
+        var tempmap = reverseMap(mode) ? map.reversed() : map;
+        var values = reverseView(mode) ? tempmap.sequencedValues().reversed() : tempmap.sequencedValues();
+        var act = callLast(mode) ? values.removeLast() : values.removeFirst();
+        assertEquals(act, exp);
+        checkContents(map, ref);
+    }
+
+    // As above, but for the sequencedEntrySet view.
+    @Test(dataProvider="viewRemoves")
+    public void testEntrySetRemoves(String label,
+                                    int mode,
+                                    SequencedMap<String, Integer> map,
+                                    List<Map.Entry<String, Integer>> baseref) {
+        var ref = new ArrayList<>(baseref);
+        var exp = refLast(mode) ? ref.remove(ref.size() - 1) : ref.remove(0);
+        var tempmap = reverseMap(mode) ? map.reversed() : map;
+        var entrySet = reverseView(mode) ? tempmap.sequencedEntrySet().reversed() : tempmap.sequencedEntrySet();
+        var act = callLast(mode) ? entrySet.removeLast() : entrySet.removeFirst();
+        assertEquals(act, exp);
+        checkContents(map, ref);
+    }
+
+    // As above, but for the sequencedKeySet of an empty map.
+    @Test(dataProvider="emptyViewRemoves")
+    public void testEmptyKeySetRemoves(String label,
+                                       int mode,
+                                       SequencedMap<String, Integer> map,
+                                       List<Map.Entry<String, Integer>> baseref) {
+        var tempmap = reverseMap(mode) ? map.reversed() : map;
+        var keySet = reverseView(mode) ? tempmap.sequencedKeySet().reversed() : tempmap.sequencedKeySet();
+        if (callLast(mode))
+            assertThrows(NSEE, () -> keySet.removeLast());
+        else
+            assertThrows(NSEE, () -> keySet.removeFirst());
+        checkContents(map, baseref);
+
+    }
+
+    // As above, but for the sequencedValues view.
+    @Test(dataProvider="emptyViewRemoves")
+    public void testEmptyValuesRemoves(String label,
+                                       int mode,
+                                       SequencedMap<String, Integer> map,
+                                       List<Map.Entry<String, Integer>> baseref) {
+        var tempmap = reverseMap(mode) ? map.reversed() : map;
+        var values = reverseView(mode) ? tempmap.sequencedValues().reversed() : tempmap.sequencedValues();
+        if (callLast(mode))
+            assertThrows(NSEE, () -> values.removeLast());
+        else
+            assertThrows(NSEE, () -> values.removeFirst());
+        checkContents(map, baseref);
+    }
+
+    // As above, but for the sequencedEntrySet view.
+    @Test(dataProvider="emptyViewRemoves")
+    public void testEmptyEntrySetRemoves(String label,
+                                         int mode,
+                                         SequencedMap<String, Integer> map,
+                                         List<Map.Entry<String, Integer>> baseref) {
+        var tempmap = reverseMap(mode) ? map.reversed() : map;
+        var entrySet = reverseView(mode) ? tempmap.sequencedEntrySet().reversed() : tempmap.sequencedEntrySet();
+        if (callLast(mode))
+            assertThrows(NSEE, () -> entrySet.removeLast());
+        else
+            assertThrows(NSEE, () -> entrySet.removeFirst());
+        checkContents(map, baseref);
+    }
+
+    // Test that addFirst/addLast on the sequencedKeySetView throw UnsupportedOperationException.
+    @Test(dataProvider="viewAddThrows")
+    public void testKeySetAddThrows(String label,
+                                    int mode,
+                                    SequencedMap<String, Integer> map,
+                                    List<Map.Entry<String, Integer>> baseref) {
+        var tempmap = reverseMap(mode) ? map.reversed() : map;
+        var keySet = reverseView(mode) ? tempmap.sequencedKeySet().reversed() : tempmap.sequencedKeySet();
+        if (callLast(mode))
+            assertThrows(UOE, () -> keySet.addLast("x"));
+        else
+            assertThrows(UOE, () -> keySet.addFirst("x"));
+        checkContents(map, baseref);
+    }
+
+    // As above, but for the sequencedValues view.
+    @Test(dataProvider="viewAddThrows")
+    public void testValuesAddThrows(String label,
+                                    int mode,
+                                    SequencedMap<String, Integer> map,
+                                    List<Map.Entry<String, Integer>> baseref) {
+        var tempmap = reverseMap(mode) ? map.reversed() : map;
+        var values = reverseView(mode) ? tempmap.sequencedValues().reversed() : tempmap.sequencedValues();
+        if (callLast(mode))
+            assertThrows(UOE, () -> values.addLast(99));
+        else
+            assertThrows(UOE, () -> values.addFirst(99));
+        checkContents(map, baseref);
+    }
+
+    // As above, but for the sequencedEntrySet view.
+    @Test(dataProvider="viewAddThrows")
+    public void testEntrySetAddThrows(String label,
+                                      int mode,
+                                      SequencedMap<String, Integer> map,
+                                      List<Map.Entry<String, Integer>> baseref) {
+        var tempmap = reverseMap(mode) ? map.reversed() : map;
+        var entrySet = reverseView(mode) ? tempmap.sequencedEntrySet().reversed() : tempmap.sequencedEntrySet();
+        if (callLast(mode))
+            assertThrows(UOE, () -> entrySet.addLast(Map.entry("x", 99)));
+        else
+            assertThrows(UOE, () -> entrySet.addFirst(Map.entry("x", 99)));
+        checkContents(map, baseref);
+    }
+}
diff --git a/test/jdk/java/util/SequencedCollection/SimpleDeque.java b/test/jdk/java/util/SequencedCollection/SimpleDeque.java
new file mode 100644
index 00000000000..c1ed07210fb
--- /dev/null
+++ b/test/jdk/java/util/SequencedCollection/SimpleDeque.java
@@ -0,0 +1,200 @@
+/*
+ * Copyright (c) 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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.util.*;
+
+/**
+ * A complete Deque implementation that inherits the reversed() method
+ * from SequencedCollection. Useful for testing ReverseOrderDequeView.
+ * Underlying implementation provided by ArrayDeque.
+ */
+public class SimpleDeque<E> implements Deque<E> {
+
+    final Deque<E> deque;
+
+    public SimpleDeque() {
+        deque = new ArrayDeque<>();
+    }
+
+    public SimpleDeque(Collection<? extends E> c) {
+        deque = new ArrayDeque<>(c);
+    }
+
+    // ========== Object ==========
+
+    public boolean equals(Object o) {
+        return deque.equals(o);
+    }
+
+    public int hashCode() {
+        return deque.hashCode();
+    }
+
+    public String toString() {
+        return deque.toString();
+    }
+
+    // ========== Collection ==========
+
+    public boolean add(E e) {
+        return deque.add(e);
+    }
+
+    public boolean addAll(Collection<? extends E> c) {
+        return deque.addAll(c);
+    }
+
+    public void clear() {
+        deque.clear();
+    }
+
+    public boolean contains(Object o) {
+        return deque.contains(o);
+    }
+
+    public boolean containsAll(Collection<?> c) {
+        return deque.containsAll(c);
+    }
+
+    public boolean isEmpty() {
+        return deque.isEmpty();
+    }
+
+    public Iterator<E> iterator() {
+        return deque.iterator();
+    }
+
+    public boolean remove(Object o) {
+        return deque.remove(o);
+    }
+
+    public boolean removeAll(Collection<?> c) {
+        return deque.removeAll(c);
+    }
+
+    public boolean retainAll(Collection<?> c) {
+        return deque.retainAll(c);
+    }
+
+    public int size() {
+        return deque.size();
+    }
+
+    public Object[] toArray() {
+        return deque.toArray();
+    }
+
+    public <T> T[] toArray(T[] a) {
+        return deque.toArray(a);
+    }
+
+    // ========== Deque ==========
+
+    public void addFirst(E e) {
+        deque.addFirst(e);
+    }
+
+    public void addLast(E e) {
+        deque.addLast(e);
+    }
+
+    public boolean offerFirst(E e) {
+        return deque.offerFirst(e);
+    }
+
+    public boolean offerLast(E e) {
+        return deque.offerLast(e);
+    }
+
+    public E removeFirst() {
+        return deque.removeFirst();
+    }
+
+    public E removeLast() {
+        return deque.removeLast();
+    }
+
+    public E pollFirst() {
+        return deque.pollFirst();
+    }
+
+    public E pollLast() {
+        return deque.pollLast();
+    }
+
+    public E getFirst() {
+        return deque.getFirst();
+    }
+
+    public E getLast() {
+        return deque.getLast();
+    }
+
+    public E peekFirst() {
+        return deque.peekFirst();
+    }
+
+    public E peekLast() {
+        return deque.peekLast();
+    }
+
+    public boolean removeFirstOccurrence(Object o) {
+        return deque.removeFirstOccurrence(o);
+    }
+
+    public boolean removeLastOccurrence(Object o) {
+        return deque.removeLastOccurrence(o);
+    }
+
+    public boolean offer(E e) {
+        return deque.offer(e);
+    }
+
+    public E remove() {
+        return deque.remove();
+    }
+
+    public E poll() {
+        return deque.poll();
+    }
+
+    public E element() {
+        return deque.element();
+    }
+
+    public E peek() {
+        return deque.peek();
+    }
+
+    public void push(E e) {
+        deque.push(e);
+    }
+
+    public E pop() {
+        return deque.pop();
+    }
+
+    public Iterator<E> descendingIterator() {
+        return deque.descendingIterator();
+    }
+}
diff --git a/test/jdk/java/util/SequencedCollection/SimpleList.java b/test/jdk/java/util/SequencedCollection/SimpleList.java
new file mode 100644
index 00000000000..f717bf05b44
--- /dev/null
+++ b/test/jdk/java/util/SequencedCollection/SimpleList.java
@@ -0,0 +1,152 @@
+/*
+ * Copyright (c) 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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.util.*;
+
+/**
+ * A complete List implementation that inherits the reversed() method
+ * from List. Useful for testing ReverseOrderListView.
+ * Underlying implementation provided by ArrayList.
+ */
+public class SimpleList<E> implements List<E> {
+
+    final List<E> list;
+
+    public SimpleList() {
+        list = new ArrayList<>();
+    }
+
+    public SimpleList(Collection<? extends E> c) {
+        list = new ArrayList<>(c);
+    }
+
+    // ========== Object ==========
+
+    public boolean equals(Object o) {
+        return list.equals(o);
+    }
+
+    public int hashCode() {
+        return list.hashCode();
+    }
+
+    public String toString() {
+        return list.toString();
+    }
+
+    // ========== Collection ==========
+
+    public boolean add(E e) {
+        return list.add(e);
+    }
+
+    public boolean addAll(Collection<? extends E> c) {
+        return list.addAll(c);
+    }
+
+    public void clear() {
+        list.clear();
+    }
+
+    public boolean contains(Object o) {
+        return list.contains(o);
+    }
+
+    public boolean containsAll(Collection<?> c) {
+        return list.containsAll(c);
+    }
+
+    public boolean isEmpty() {
+        return list.isEmpty();
+    }
+
+    public Iterator<E> iterator() {
+        return list.iterator();
+    }
+
+    public boolean remove(Object o) {
+        return list.remove(o);
+    }
+
+    public boolean removeAll(Collection<?> c) {
+        return list.removeAll(c);
+    }
+
+    public boolean retainAll(Collection<?> c) {
+        return list.retainAll(c);
+    }
+
+    public int size() {
+        return list.size();
+    }
+
+    public Object[] toArray() {
+        return list.toArray();
+    }
+
+    public <T> T[] toArray(T[] a) {
+        return list.toArray(a);
+    }
+
+    // ========== List ==========
+
+    public void add(int index, E element) {
+        list.add(index, element);
+    }
+
+    public boolean addAll(int index, Collection<? extends E> c) {
+        return list.addAll(index, c);
+    }
+
+    public E get(int index) {
+        return list.get(index);
+    }
+
+    public int indexOf(Object o) {
+        return list.indexOf(o);
+    }
+
+    public int lastIndexOf(Object o) {
+        return list.lastIndexOf(o);
+    }
+
+    public ListIterator<E> listIterator() {
+        return list.listIterator();
+    }
+
+    public ListIterator<E> listIterator(int index) {
+        return list.listIterator(index);
+    }
+
+    public E remove(int index) {
+        return list.remove(index);
+    }
+
+    public E set(int index, E element) {
+        return list.set(index, element);
+    }
+
+    public List<E> subList(int fromIndex, int toIndex) {
+        return list.subList(fromIndex, toIndex);
+    }
+}
diff --git a/test/jdk/java/util/SequencedCollection/SimpleSortedMap.java b/test/jdk/java/util/SequencedCollection/SimpleSortedMap.java
new file mode 100644
index 00000000000..4c439d51e28
--- /dev/null
+++ b/test/jdk/java/util/SequencedCollection/SimpleSortedMap.java
@@ -0,0 +1,134 @@
+/*
+ * Copyright (c) 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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.util.*;
+
+/**
+ * A SortedMap implementation that does not implement NavigableMap. Useful for
+ * testing ReverseOrderSortedMapView. Underlying implementation provided by TreeMap.
+ */
+public class SimpleSortedMap<K,V> implements SortedMap<K,V> {
+    final SortedMap<K,V> map;
+
+    public SimpleSortedMap() {
+        map = new TreeMap<>();
+    }
+
+    public SimpleSortedMap(Comparator<? super K> comparator) {
+        map = new TreeMap<>(comparator);
+    }
+
+    public SimpleSortedMap(Map<? extends K,? extends V> m) {
+        map = new TreeMap<>(m);
+    }
+
+    // ========== Object ==========
+
+    public boolean equals(Object o) {
+        return map.equals(o);
+    }
+
+    public int hashCode() {
+        return map.hashCode();
+    }
+
+    public String toString() {
+        return map.toString();
+    }
+
+    // ========== Map ==========
+
+    public void clear() {
+        map.clear();
+    }
+
+    public boolean containsKey(Object key) {
+        return map.containsKey(key);
+    }
+
+    public boolean containsValue(Object value) {
+        return map.containsValue(value);
+    }
+
+    public Set<Map.Entry<K,V>> entrySet() {
+        return map.entrySet();
+    }
+
+    public V get(Object key) {
+        return map.get(key);
+    }
+
+    public boolean isEmpty() {
+        return map.isEmpty();
+    }
+
+    public Set<K> keySet() {
+        return map.keySet();
+    }
+
+    public V put(K key, V value) {
+        return map.put(key, value);
+    }
+
+    public void putAll(Map<? extends K,? extends V> m) {
+        map.putAll(m);
+    }
+
+    public V remove(Object key) {
+        return map.remove(key);
+    }
+
+    public int size() {
+        return map.size();
+    }
+
+    public Collection<V> values() {
+        return map.values();
+    }
+
+    // ========== SortedMap ==========
+
+    public Comparator<? super K> comparator() {
+        return map.comparator();
+    }
+
+    public K firstKey() {
+        return map.firstKey();
+    }
+
+    public SortedMap<K,V> headMap(K toKey) {
+        return map.headMap(toKey);
+    }
+
+    public K lastKey() {
+        return map.lastKey();
+    }
+
+    public SortedMap<K,V> subMap(K fromKey, K toKey) {
+        return map.subMap(fromKey, toKey);
+    }
+
+    public SortedMap<K,V> tailMap(K fromKey) {
+        return map.tailMap(fromKey);
+    }
+}
diff --git a/test/jdk/java/util/SequencedCollection/SimpleSortedSet.java b/test/jdk/java/util/SequencedCollection/SimpleSortedSet.java
new file mode 100644
index 00000000000..6ca2156a47d
--- /dev/null
+++ b/test/jdk/java/util/SequencedCollection/SimpleSortedSet.java
@@ -0,0 +1,139 @@
+/*
+ * Copyright (c) 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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.util.*;
+
+/**
+ * A SortedSet implementation that does not implement NavigableSet. Useful for
+ * testing ReverseOrderSortedSetView. Underlying implementation provided by TreeSet.
+ */
+public class SimpleSortedSet<E> implements SortedSet<E> {
+
+    final SortedSet<E> set;
+
+    public SimpleSortedSet() {
+        set = new TreeSet<E>();
+    }
+
+    public SimpleSortedSet(Collection<? extends E> c) {
+        set = new TreeSet<>(c);
+    }
+
+    public SimpleSortedSet(Comparator<? super E> comparator) {
+        set = new TreeSet<>(comparator);
+    }
+
+    // ========== Object ==========
+
+    public boolean equals(Object o) {
+        return set.equals(o);
+    }
+
+    public int hashCode() {
+        return set.hashCode();
+    }
+
+    public String toString() {
+        return set.toString();
+    }
+
+    // ========== Collection ==========
+
+    public boolean add(E e) {
+        return set.add(e);
+    }
+
+    public boolean addAll(Collection<? extends E> c) {
+        return set.addAll(c);
+    }
+
+    public void clear() {
+        set.clear();
+    }
+
+    public boolean contains(Object o) {
+        return set.contains(o);
+    }
+
+    public boolean containsAll(Collection<?> c) {
+        return set.containsAll(c);
+    }
+
+    public boolean isEmpty() {
+        return set.isEmpty();
+    }
+
+    public Iterator<E> iterator() {
+        return set.iterator();
+    }
+
+    public boolean remove(Object o) {
+        return set.remove(o);
+    }
+
+    public boolean removeAll(Collection<?> c) {
+        return set.removeAll(c);
+    }
+
+    public boolean retainAll(Collection<?> c) {
+        return set.retainAll(c);
+    }
+
+    public int size() {
+        return set.size();
+    }
+
+    public Object[] toArray() {
+        return set.toArray();
+    }
+
+    public <T> T[] toArray(T[] a) {
+        return set.toArray(a);
+    }
+
+    // ========== SortedSet ==========
+
+    public Comparator<? super E> comparator() {
+        return set.comparator();
+    }
+
+    public E first() {
+        return set.first();
+    }
+
+    public SortedSet<E> headSet(E toElement) {
+        return set.headSet(toElement);
+    }
+
+    public E last() {
+        return set.last();
+    }
+
+    public SortedSet<E> subSet(E fromElement, E toElement) {
+        return set.subSet(fromElement, toElement);
+    }
+
+    public SortedSet<E> tailSet(E fromElement) {
+        return set.tailSet(fromElement);
+    }
+}
diff --git a/test/jdk/jdk/internal/util/ArraysSupport/Reverse.java b/test/jdk/jdk/internal/util/ArraysSupport/Reverse.java
new file mode 100644
index 00000000000..6e0ebd06257
--- /dev/null
+++ b/test/jdk/jdk/internal/util/ArraysSupport/Reverse.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (c) 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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @bug 8266571
+ * @modules java.base/jdk.internal.util
+ * @run testng Reverse
+ * @summary Tests for ArraysSupport.reverse
+ */
+
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertSame;
+
+import java.util.Arrays;
+import jdk.internal.util.ArraysSupport;
+
+public class Reverse {
+    @DataProvider(name = "data")
+    public Object[][] data() {
+        return new Object[][][] {
+            // pairs of actual, expected
+            {
+                { },
+                { }
+            }, {
+                { "a" },
+                { "a" }
+            }, {
+                { "a", "b" },
+                { "b", "a" }
+            }, {
+                { "a", "b", "c" },
+                { "c", "b", "a" }
+            }, {
+                { "a", "b", "c", "d" },
+                { "d", "c", "b", "a" }
+            }, {
+                { "a", "b", "c", "d", "e" },
+                { "e", "d", "c", "b", "a" }
+            }
+        };
+    }
+
+    @Test(dataProvider = "data")
+    public void testReverse(Object[] actual, Object[] expected) {
+        Object[] r = ArraysSupport.reverse(actual);
+        assertSame(r, actual);
+        assertEquals(actual.length, expected.length);
+        for (int i = 0; i < actual.length; i++) {
+            assertEquals(actual[i], expected[i],
+                "mismatch: actual=" + Arrays.asList(actual) +
+                " expected=" + Arrays.asList(expected) + " at index " + i);
+        }
+    }
+}
diff --git a/test/langtools/tools/javac/api/TestJavacTaskScanner.java b/test/langtools/tools/javac/api/TestJavacTaskScanner.java
index e8110396dac..e302247bd68 100644
--- a/test/langtools/tools/javac/api/TestJavacTaskScanner.java
+++ b/test/langtools/tools/javac/api/TestJavacTaskScanner.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2005, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2005, 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
@@ -102,7 +102,7 @@ public class TestJavacTaskScanner extends ToolTester {
         System.out.println("#allMembers: " + numAllMembers);
 
         check(numTokens, "#Tokens", 1054);
-        check(numParseTypeElements, "#parseTypeElements", 158);
+        check(numParseTypeElements, "#parseTypeElements", 180);
         check(numAllMembers, "#allMembers", 52);
     }
 
diff --git a/test/langtools/tools/javac/processing/model/type/BoundsTest.java b/test/langtools/tools/javac/processing/model/type/BoundsTest.java
index b7e9121a956..f72fc6461b9 100644
--- a/test/langtools/tools/javac/processing/model/type/BoundsTest.java
+++ b/test/langtools/tools/javac/processing/model/type/BoundsTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 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
@@ -70,7 +70,7 @@ public class BoundsTest {
     };
     private static final String[] Single_supers = {
         "java.lang.Object",
-        "java.util.Collection"
+        "java.util.SequencedCollection"
     };
 
     private static final String NoBounds_name = "NoBoundsTest.java";