8266571: Sequenced Collections

Reviewed-by: alanb
This commit is contained in:
Stuart Marks 2023-04-25 15:19:08 +00:00
parent bad6aa68e4
commit 17ce0976e4
42 changed files with 7060 additions and 150 deletions

View file

@ -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); }
}
}

View file

@ -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">

View file

@ -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}
*/

View file

@ -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

View file

@ -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

View file

@ -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);
}
}

View file

@ -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

View file

@ -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());
}

View file

@ -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);
}
}
}

View file

@ -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();
}
}

View file

@ -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");
}
}
}

View file

@ -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.
*

View file

@ -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

View file

@ -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();
}
}

View file

@ -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();
}
}

View file

@ -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);
}
}

View file

@ -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);
}
}
}

View file

@ -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();
}
}
}

View file

@ -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();
}
}
}

View file

@ -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;
}
}

View file

@ -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();
}
}

View file

@ -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();
}

View file

@ -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);
}
}

View file

@ -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);
}
}

View file

@ -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

View file

@ -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.)

View file

@ -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

View file

@ -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

View file

@ -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")