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

View File

@ -24,6 +24,8 @@
*/
package jdk.internal.util;
import java.util.Arrays;
import java.util.Collection;
import jdk.internal.access.JavaLangAccess;
import jdk.internal.access.SharedSecrets;
import jdk.internal.misc.Unsafe;
@ -755,4 +757,53 @@ public class ArraysSupport {
return minLength;
}
}
/**
* Reverses the elements of an array in-place.
*
* @param <T> the array component type
* @param a the array to be reversed
* @return the reversed array, always the same array as the argument
*/
public static <T> T[] reverse(T[] a) {
int limit = a.length / 2;
for (int i = 0, j = a.length - 1; i < limit; i++, j--) {
T t = a[i];
a[i] = a[j];
a[j] = t;
}
return a;
}
/**
* Dump the contents of the given collection into the given array, in reverse order.
* This mirrors the semantics of Collection.toArray(T[]) in regard to reusing the given
* array, appending null if necessary, or allocating a new array of the same component type.
* <p>
* A constraint is that this method should issue exactly one method call on the collection
* to obtain the elements and the size. Having a separate size() call or using an Iterator
* could result in errors if the collection changes size between calls. This implies that
* the elements need to be obtained via a single call to one of the toArray() methods.
* This further implies allocating memory proportional to the number of elements and
* making an extra copy, but this seems unavoidable.
* <p>
* An obvious approach would be simply to call coll.toArray(array) and then reverse the
* order of the elements. This doesn't work, because if given array is sufficiently long,
* we cannot tell how many elements were copied into it and thus there is no way to reverse
* the right set of elements while leaving the remaining array elements undisturbed.
*
* @throws ArrayStoreException if coll contains elements that can't be stored in the array
*/
public static <T> T[] toArrayReversed(Collection<?> coll, T[] array) {
T[] newArray = reverse(coll.toArray(Arrays.copyOfRange(array, 0, 0)));
if (newArray.length > array.length) {
return newArray;
} else {
System.arraycopy(newArray, 0, array, 0, newArray.length);
if (array.length > newArray.length) {
array[newArray.length] = null;
}
return array;
}
}
}

View File

@ -171,6 +171,7 @@ jdk_collections_core = \
java/util/Map \
java/util/NavigableMap \
java/util/PriorityQueue \
java/util/SequencedCollection \
java/util/TimSort \
java/util/TreeMap \
java/util/Vector \

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2005, 2020, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2005, 2023, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -137,9 +137,13 @@ public class MOAT {
testMap(Collections.synchronizedNavigableMap(new TreeMap<Integer,Integer>()));
// Unmodifiable wrappers
testImmutableSet(unmodifiableSet(new HashSet<>(Arrays.asList(1,2,3))));
testImmutableSet(unmodifiableSet(new HashSet<>(Arrays.asList(1,2,3))), 99);
testImmutableList(unmodifiableList(Arrays.asList(1,2,3)));
testImmutableMap(unmodifiableMap(Collections.singletonMap(1,2)));
testImmutableSeqColl(unmodifiableSequencedCollection(Arrays.asList(1,2,3)), 99);
testImmutableSeqColl(unmodifiableSequencedSet(new LinkedHashSet<>(Arrays.asList(1,2,3))), 99);
var lhm = new LinkedHashMap<Integer,Integer>(); lhm.put(1,2); lhm.put(3, 4);
testImmutableSeqMap(unmodifiableSequencedMap(lhm));
testCollMutatorsAlwaysThrow(unmodifiableSet(new HashSet<>(Arrays.asList(1,2,3))));
testCollMutatorsAlwaysThrow(unmodifiableSet(Collections.emptySet()));
testEmptyCollMutatorsAlwaysThrow(unmodifiableSet(Collections.emptySet()));
@ -169,7 +173,7 @@ public class MOAT {
testEmptySet(Collections.emptySet());
testEmptySet(Collections.emptySortedSet());
testEmptySet(Collections.emptyNavigableSet());
testImmutableSet(emptySet);
testImmutableSet(emptySet, 99);
List<Integer> emptyList = emptyList();
testCollection(emptyList);
@ -194,7 +198,7 @@ public class MOAT {
Set<Integer> singletonSet = singleton(1);
equal(singletonSet.size(), 1);
testCollection(singletonSet);
testImmutableSet(singletonSet);
testImmutableSet(singletonSet, 99);
List<Integer> singletonList = singletonList(1);
equal(singletonList.size(), 1);
@ -322,20 +326,20 @@ public class MOAT {
Set.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10),
Set.of(integerArray))) {
testCollection(set);
testImmutableSet(set);
testImmutableSet(set, 99);
testCollMutatorsAlwaysThrow(set);
}
Set<Integer> setCopy = Set.copyOf(Arrays.asList(1, 2, 3));
testCollection(setCopy);
testImmutableSet(setCopy);
testImmutableSet(setCopy, 99);
testCollMutatorsAlwaysThrow(setCopy);
Set<Integer> setCollected = Stream.of(1, 1, 2, 3, 2, 3)
.collect(Collectors.toUnmodifiableSet());
equal(setCollected, Set.of(1, 2, 3));
testCollection(setCollected);
testImmutableSet(setCollected);
testImmutableSet(setCollected, 99);
testCollMutatorsAlwaysThrow(setCollected);
// Immutable Map
@ -462,12 +466,12 @@ public class MOAT {
testEmptyIterator(((NavigableSet<T>)c).descendingIterator());
}
private static void testImmutableCollection(final Collection<Integer> c) {
private static <T> void testImmutableCollection(final Collection<T> c, T t) {
THROWS(UnsupportedOperationException.class,
() -> c.add(99),
() -> c.addAll(singleton(99)));
() -> c.add(t),
() -> c.addAll(singleton(t)));
if (! c.isEmpty()) {
final Integer first = c.iterator().next();
final T first = c.iterator().next();
THROWS(UnsupportedOperationException.class,
() -> c.clear(),
() -> c.remove(first),
@ -476,13 +480,36 @@ public class MOAT {
}
}
private static void testImmutableSet(final Set<Integer> c) {
testImmutableCollection(c);
private static <T> void testImmutableSeqColl(final SequencedCollection<T> c, T t) {
SequencedCollection<T> r = c.reversed();
testImmutableCollection(c, t);
testImmutableCollection(r, t);
THROWS(UnsupportedOperationException.class,
() -> c.addFirst(t),
() -> c.addLast(t),
() -> r.addFirst(t),
() -> r.addLast(t));
if (! c.isEmpty()) {
THROWS(UnsupportedOperationException.class,
() -> c.removeFirst(),
() -> c.removeLast(),
() -> r.removeFirst(),
() -> r.removeLast());
}
}
private static <T> void testImmutableSet(final Set<T> c, T t) {
testImmutableCollection(c, t);
}
private static <T> void testImmutableSeqSet(final SequencedSet<T> c, T t) {
testImmutableSeqColl(c, t);
}
private static void testImmutableList(final List<Integer> c) {
testList(c);
testImmutableCollection(c);
testImmutableCollection(c, 42);
testImmutableSeqColl(c, 42);
THROWS(UnsupportedOperationException.class,
() -> c.set(0,42),
() -> c.add(0,42),
@ -606,6 +633,15 @@ public class MOAT {
check(! m.containsKey(1));
}
private static void testImmutableMapEntry(final Map.Entry<Integer,Integer> me) {
Integer key = me.getKey();
Integer val = me.getValue();
THROWS(UnsupportedOperationException.class,
() -> me.setValue(3));
equal(key, me.getKey());
equal(val, me.getValue());
}
private static void testImmutableMap(final Map<Integer,Integer> m) {
THROWS(UnsupportedOperationException.class,
() -> m.put(1,1),
@ -615,18 +651,39 @@ public class MOAT {
THROWS(UnsupportedOperationException.class,
() -> m.remove(first),
() -> m.clear());
final Map.Entry<Integer,Integer> me
= m.entrySet().iterator().next();
Integer key = me.getKey();
Integer val = me.getValue();
THROWS(UnsupportedOperationException.class,
() -> me.setValue(3));
equal(key, me.getKey());
equal(val, me.getValue());
testImmutableMapEntry(m.entrySet().iterator().next());
}
testImmutableSet(m.keySet());
testImmutableCollection(m.values());
//testImmutableSet(m.entrySet());
testImmutableSet(m.keySet(), 99);
testImmutableCollection(m.values(), 99);
testImmutableSet(m.entrySet(), Map.entry(42, 43));
}
private static void testImmutableSeqMap(final SequencedMap<Integer,Integer> m) {
SequencedMap<Integer,Integer> r = m.reversed();
testImmutableMap(m);
testImmutableMap(r);
THROWS(UnsupportedOperationException.class,
() -> m.putFirst(0, 0),
() -> m.putLast(0, 0),
() -> r.putFirst(0, 0),
() -> r.putLast(0, 0));
if (! m.isEmpty()) {
THROWS(UnsupportedOperationException.class,
() -> m.pollFirstEntry(),
() -> m.pollLastEntry(),
() -> r.pollFirstEntry(),
() -> r.pollLastEntry());
testImmutableMapEntry(m.sequencedEntrySet().getFirst());
testImmutableMapEntry(r.sequencedEntrySet().getFirst());
testImmutableMapEntry(m.sequencedEntrySet().reversed().getFirst());
testImmutableMapEntry(r.sequencedEntrySet().reversed().getFirst());
}
testImmutableSeqSet(m.sequencedKeySet(), 99);
testImmutableSeqColl(m.sequencedValues(), 99);
testImmutableSeqSet(m.sequencedEntrySet(), Map.entry(42, 43));
testImmutableSeqSet(r.sequencedKeySet(), 99);
testImmutableSeqColl(r.sequencedValues(), 99);
testImmutableSeqSet(r.sequencedEntrySet(), Map.entry(42, 43));
}
private static void clear(Map<?,?> m) {

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2013, 2023, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -33,14 +33,13 @@ import java.util.Collection;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.Objects;
import java.util.TreeMap;
import java.util.TreeSet;
import org.testng.annotations.Test;
import org.testng.annotations.DataProvider;
import static org.testng.Assert.assertFalse;
import static org.testng.Assert.assertTrue;
@Test(groups = "unit")
public class Wrappers {
@ -66,9 +65,11 @@ public class Wrappers {
}
cases.add(new Object[] { Collections.unmodifiableCollection(seedList) });
cases.add(new Object[] { Collections.unmodifiableSequencedCollection(seedList) });
cases.add(new Object[] { Collections.unmodifiableList(seedList) });
cases.add(new Object[] { Collections.unmodifiableList(seedRandomAccess) });
cases.add(new Object[] { Collections.unmodifiableSet(seedSet) });
cases.add(new Object[] { Collections.unmodifiableSequencedSet(seedSet) });
cases.add(new Object[] { Collections.unmodifiableSortedSet(seedSet) });
cases.add(new Object[] { Collections.unmodifiableNavigableSet(seedSet) });
@ -77,6 +78,24 @@ public class Wrappers {
cases.add(new Object[] { Collections.unmodifiableMap(seedMap).entrySet() });
cases.add(new Object[] { Collections.unmodifiableMap(seedMap).keySet() });
cases.add(new Object[] { Collections.unmodifiableMap(seedMap).values() });
cases.add(new Object[] { Collections.unmodifiableSequencedMap(seedMap).entrySet() });
cases.add(new Object[] { Collections.unmodifiableSequencedMap(seedMap).keySet() });
cases.add(new Object[] { Collections.unmodifiableSequencedMap(seedMap).values() });
cases.add(new Object[] { Collections.unmodifiableSequencedMap(seedMap).reversed().entrySet() });
cases.add(new Object[] { Collections.unmodifiableSequencedMap(seedMap).reversed().keySet() });
cases.add(new Object[] { Collections.unmodifiableSequencedMap(seedMap).reversed().values() });
cases.add(new Object[] { Collections.unmodifiableSequencedMap(seedMap).sequencedEntrySet() });
cases.add(new Object[] { Collections.unmodifiableSequencedMap(seedMap).sequencedKeySet() });
cases.add(new Object[] { Collections.unmodifiableSequencedMap(seedMap).sequencedValues() });
cases.add(new Object[] { Collections.unmodifiableSequencedMap(seedMap).sequencedEntrySet().reversed() });
cases.add(new Object[] { Collections.unmodifiableSequencedMap(seedMap).sequencedKeySet().reversed() });
cases.add(new Object[] { Collections.unmodifiableSequencedMap(seedMap).sequencedValues().reversed() });
cases.add(new Object[] { Collections.unmodifiableSequencedMap(seedMap).reversed().sequencedEntrySet() });
cases.add(new Object[] { Collections.unmodifiableSequencedMap(seedMap).reversed().sequencedKeySet() });
cases.add(new Object[] { Collections.unmodifiableSequencedMap(seedMap).reversed().sequencedValues() });
cases.add(new Object[] { Collections.unmodifiableSequencedMap(seedMap).reversed().sequencedEntrySet().reversed() });
cases.add(new Object[] { Collections.unmodifiableSequencedMap(seedMap).reversed().sequencedKeySet().reversed() });
cases.add(new Object[] { Collections.unmodifiableSequencedMap(seedMap).reversed().sequencedValues().reversed() });
cases.add(new Object[] { Collections.unmodifiableSortedMap(seedMap).entrySet() });
cases.add(new Object[] { Collections.unmodifiableSortedMap(seedMap).keySet() });
cases.add(new Object[] { Collections.unmodifiableSortedMap(seedMap).values() });
@ -136,11 +155,14 @@ public class Wrappers {
@Test(dataProvider = "collections")
public static void testAllDefaultMethodsOverridden(Collection c) throws NoSuchMethodException {
Class cls = c.getClass();
var notOverridden = new ArrayList<Method>();
for (Method m: defaultMethods) {
Method m2 = cls.getMethod(m.getName(), m.getParameterTypes());
// default had been override
assertFalse(m2.isDefault(), cls.getCanonicalName());
if (m2.isDefault()) {
notOverridden.add(m);
}
}
assertTrue(notOverridden.isEmpty(), cls.getName() + " does not override " + notOverridden);
}
}

View File

@ -0,0 +1,857 @@
/*
* Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
import java.io.*;
import java.util.*;
import java.util.concurrent.CopyOnWriteArrayList;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertSame;
import static org.testng.Assert.assertThrows;
import static org.testng.Assert.assertTrue;
/*
* @test
* @bug 8266571
* @summary Basic tests for SequencedCollection
* @modules java.base/java.util:open
* @build SimpleDeque SimpleList SimpleSortedSet
* @run testng Basic
*/
// TODO test that remove(obj) with duplicates removes the right element
public class Basic {
// ========== Data Providers ==========
static final List<String> ORIGINAL = List.of("a", "b", "c", "d", "e", "f", "g");
static List<String> cklist(List<String> contents) {
return Collections.checkedList(contents, String.class);
}
static NavigableSet<String> cknav(NavigableSet<String> set) {
return Collections.checkedNavigableSet(set, String.class);
}
static SortedSet<String> cksorted(SortedSet<String> set) {
return Collections.checkedSortedSet(set, String.class);
}
static SequencedSet<String> setFromMap(List<String> contents) {
var lhm = new LinkedHashMap<String, Boolean>();
var ss = Collections.newSequencedSetFromMap(lhm);
ss.addAll(contents);
return ss;
}
static SequencedCollection<String> ucoll(SequencedCollection<String> coll) {
return Collections.unmodifiableSequencedCollection(coll);
}
static SequencedCollection<String> ulist(List<String> list) {
return Collections.unmodifiableList(list);
}
static NavigableSet<String> unav(NavigableSet<String> set) {
return Collections.unmodifiableNavigableSet(set);
}
static SequencedSet<String> uset(SequencedSet<String> set) {
return Collections.unmodifiableSequencedSet(set);
}
static SortedSet<String> usorted(SortedSet<String> set) {
return Collections.unmodifiableSortedSet(set);
}
static <T> List<T> copyReversed(List<T> list) {
var r = new ArrayList<T>(list);
Collections.reverse(r);
return r;
}
@DataProvider(name="all")
public Iterator<Object[]> all() {
var result = new ArrayList<Object[]>();
populated().forEachRemaining(result::add);
empties().forEachRemaining(result::add);
return result.iterator();
}
@DataProvider(name="populated")
public Iterator<Object[]> populated() {
return Arrays.asList(
new Object[] { "ArrayDeque", new ArrayDeque<>(ORIGINAL), ORIGINAL },
new Object[] { "ArrayList", new ArrayList<>(ORIGINAL), ORIGINAL },
new Object[] { "AsList", Arrays.asList(ORIGINAL.toArray()), ORIGINAL },
new Object[] { "COWAL", new CopyOnWriteArrayList<>(ORIGINAL), ORIGINAL },
new Object[] { "LinkedHashSet", new LinkedHashSet<>(ORIGINAL), ORIGINAL },
new Object[] { "LinkedList", new LinkedList<>(ORIGINAL), ORIGINAL },
new Object[] { "ListOf", ORIGINAL, ORIGINAL },
new Object[] { "SetFromMap", setFromMap(ORIGINAL), ORIGINAL },
new Object[] { "SimpleDeque", new SimpleDeque<>(ORIGINAL), ORIGINAL },
new Object[] { "SimpleList", new SimpleList<>(ORIGINAL), ORIGINAL },
new Object[] { "SimpleSortedSet", new SimpleSortedSet<>(ORIGINAL), ORIGINAL },
new Object[] { "TreeSet", new TreeSet<>(ORIGINAL), ORIGINAL },
new Object[] { "UnmodColl", ucoll(new ArrayList<>(ORIGINAL)), ORIGINAL },
new Object[] { "UnmodSet", uset(new LinkedHashSet<>(ORIGINAL)), ORIGINAL }
).iterator();
}
@DataProvider(name="empties")
public Iterator<Object[]> empties() {
return Arrays.asList(
new Object[] { "ArrayDeque", new ArrayDeque<>(), List.of() },
new Object[] { "ArrayList", new ArrayList<>(), List.of() },
new Object[] { "AsList", Arrays.asList(new String[0]), List.of() },
new Object[] { "COWAL", new CopyOnWriteArrayList<>(), List.of() },
new Object[] { "EmptyList", Collections.emptyList(), List.of() },
new Object[] { "EmptyNavigableSet", Collections.emptyNavigableSet(), List.of() },
new Object[] { "EmptySortedSet", Collections.emptySortedSet(), List.of() },
new Object[] { "LinkedHashSet", new LinkedHashSet<>(), List.of() },
new Object[] { "LinkedList", new LinkedList<>(), List.of() },
new Object[] { "ListOf", List.of(), List.of() },
new Object[] { "SetFromMap", setFromMap(List.of()), List.of() },
new Object[] { "SimpleDeque", new SimpleDeque<>(), List.of() },
new Object[] { "SimpleList", new SimpleList<>(), List.of() },
new Object[] { "SimpleSortedSet", new SimpleSortedSet<>(), List.of() },
new Object[] { "TreeSet", new TreeSet<>(), List.of() },
new Object[] { "UnmodColl", ucoll(new ArrayList<>()), List.of() },
new Object[] { "UnmodSet", uset(new LinkedHashSet<>()), List.of() }
).iterator();
}
@DataProvider(name="adds")
public Iterator<Object[]> adds() {
return Arrays.asList(
new Object[] { "ArrayDeque", new ArrayDeque<>(ORIGINAL), ORIGINAL },
new Object[] { "ArrayList", new ArrayList<>(ORIGINAL), ORIGINAL },
new Object[] { "COWAL", new CopyOnWriteArrayList<>(ORIGINAL), ORIGINAL },
new Object[] { "LinkedHashSet", new LinkedHashSet<>(ORIGINAL), ORIGINAL },
new Object[] { "LinkedList", new LinkedList<>(ORIGINAL), ORIGINAL },
new Object[] { "SetFromMap", setFromMap(ORIGINAL), ORIGINAL },
new Object[] { "SimpleDeque", new SimpleDeque<>(ORIGINAL), ORIGINAL },
new Object[] { "SimpleList", new SimpleList<>(ORIGINAL), ORIGINAL }
).iterator();
}
@DataProvider(name="unpositionedAdd")
public Iterator<Object[]> unpositionedAdd() {
return Arrays.<Object[]>asList(
new Object[] { "LinkedHashSet", new LinkedHashSet<>(ORIGINAL), ORIGINAL }
).iterator();
}
@DataProvider(name="removes")
public Iterator<Object[]> removes() {
return Arrays.asList(
new Object[] { "ArrayDeque", new ArrayDeque<>(ORIGINAL), ORIGINAL },
new Object[] { "ArrayList", new ArrayList<>(ORIGINAL), ORIGINAL },
new Object[] { "COWAL", new CopyOnWriteArrayList<>(ORIGINAL), ORIGINAL },
new Object[] { "LinkedHashSet", new LinkedHashSet<>(ORIGINAL), ORIGINAL },
new Object[] { "LinkedList", new LinkedList<>(ORIGINAL), ORIGINAL },
new Object[] { "SetFromMap", setFromMap(ORIGINAL), ORIGINAL },
new Object[] { "SimpleDeque", new SimpleDeque<>(ORIGINAL), ORIGINAL },
new Object[] { "SimpleList", new SimpleList<>(ORIGINAL), ORIGINAL },
new Object[] { "SimpleSortedSet", new SimpleSortedSet<>(ORIGINAL), ORIGINAL },
new Object[] { "TreeSet", new TreeSet<>(ORIGINAL), ORIGINAL }
).iterator();
}
@DataProvider(name="emptyRemoves")
public Iterator<Object[]> emptyRemoves() {
return Arrays.asList(
new Object[] { "ArrayDeque", new ArrayDeque<>(), List.of() },
new Object[] { "ArrayList", new ArrayList<>(), List.of() },
new Object[] { "COWAL", new CopyOnWriteArrayList<>(), List.of() },
new Object[] { "LinkedHashSet", new LinkedHashSet<>(), List.of() },
new Object[] { "LinkedList", new LinkedList<>(), List.of() },
new Object[] { "SetFromMap", setFromMap(List.of()), List.of() },
new Object[] { "SimpleDeque", new SimpleDeque<>(), List.of() },
new Object[] { "SimpleList", new SimpleList<>(), List.of() },
new Object[] { "SimpleSortedSet", new SimpleSortedSet<>(), List.of() },
new Object[] { "TreeSet", new TreeSet<>(), List.of() }
).iterator();
}
@DataProvider(name="serializable")
public Iterator<Object[]> serializable() {
return Arrays.asList(
new Object[] { "ArrayDeque", new ArrayDeque<>(ORIGINAL), ORIGINAL },
new Object[] { "ArrayList", new ArrayList<>(ORIGINAL), ORIGINAL },
new Object[] { "AsList", Arrays.asList(ORIGINAL.toArray()), ORIGINAL },
new Object[] { "COWAL", new CopyOnWriteArrayList<>(ORIGINAL), ORIGINAL },
new Object[] { "LinkedHashSet", new LinkedHashSet<>(ORIGINAL), ORIGINAL },
new Object[] { "LinkedList", new LinkedList<>(ORIGINAL), ORIGINAL },
new Object[] { "ListOf", ORIGINAL, ORIGINAL },
new Object[] { "SetFromMap", setFromMap(ORIGINAL), ORIGINAL },
new Object[] { "TreeSet", new TreeSet<>(ORIGINAL), ORIGINAL },
new Object[] { "UnmodColl", ucoll(new ArrayList<>(ORIGINAL)), ORIGINAL },
new Object[] { "UnmodSet", uset(new LinkedHashSet<>(ORIGINAL)), ORIGINAL }
).iterator();
}
@DataProvider(name="notSerializable")
public Iterator<Object[]> notSerializable() {
return Arrays.asList(
new Object[] { "ArrayDeque", new ArrayDeque<>(ORIGINAL).reversed() },
new Object[] { "ArrayList", new ArrayList<>(ORIGINAL).reversed() },
new Object[] { "AsList", Arrays.asList(ORIGINAL.toArray()).reversed() },
new Object[] { "COWAL", new CopyOnWriteArrayList<>(ORIGINAL).reversed() },
new Object[] { "LinkedHashSet", new LinkedHashSet<>(ORIGINAL).reversed() },
new Object[] { "LinkedList", new LinkedList<>(ORIGINAL).reversed() },
new Object[] { "ListOf", ORIGINAL.reversed() },
new Object[] { "SetFromMap", setFromMap(ORIGINAL).reversed() },
new Object[] { "UnmodColl", ucoll(new ArrayList<>(ORIGINAL)).reversed() },
new Object[] { "UnmodSet", uset(new LinkedHashSet<>(ORIGINAL)).reversed() }
).iterator();
}
@DataProvider(name="doubleReverse")
public Iterator<Object[]> doubleReverse() {
return Arrays.asList(
new Object[] { "ArrayDeque", new ArrayDeque<>(ORIGINAL) },
new Object[] { "ArrayList", new ArrayList<>(ORIGINAL) },
new Object[] { "AsList", Arrays.asList(ORIGINAL.toArray()) },
new Object[] { "COWAL", new CopyOnWriteArrayList<>(ORIGINAL) },
new Object[] { "LinkedHashSet", new LinkedHashSet<>(ORIGINAL) },
new Object[] { "LinkedList", new LinkedList<>(ORIGINAL) },
new Object[] { "ListOf", ORIGINAL },
new Object[] { "SimpleDeque", new SimpleDeque<>(ORIGINAL) },
new Object[] { "SimpleList", new SimpleList<>(ORIGINAL) },
new Object[] { "SimpleSortedSet", new SimpleSortedSet<>(ORIGINAL) }
).iterator();
}
@DataProvider(name="unmodifiable")
public Iterator<Object[]> unmodifiable() {
return Arrays.asList(
new Object[] { "ListOf", ORIGINAL, ORIGINAL },
new Object[] { "ListOfSub", ORIGINAL.subList(1, 3), ORIGINAL.subList(1, 3) },
new Object[] { "SingleList", Collections.singletonList("a"), List.of("a") },
new Object[] { "UnmodColl", ucoll(new ArrayList<>(ORIGINAL)), ORIGINAL },
new Object[] { "UnmodList", ulist(new ArrayList<>(ORIGINAL)), ORIGINAL },
new Object[] { "UnmodNav", unav(new TreeSet<>(ORIGINAL)), ORIGINAL },
new Object[] { "UnmodSet", uset(new LinkedHashSet<>(ORIGINAL)), ORIGINAL },
new Object[] { "UnmodSorted", usorted(new TreeSet<>(ORIGINAL)), ORIGINAL }
).iterator();
}
@DataProvider(name="checkedList")
public Iterator<Object[]> checkedList() {
return Arrays.<Object[]>asList(
new Object[] { "ChkList", cklist(new ArrayList<>(ORIGINAL)), ORIGINAL }
).iterator();
}
@DataProvider(name="checkedNavSet")
public Iterator<Object[]> checkedNavSet() {
return Arrays.<Object[]>asList(
new Object[] { "ChkNav", cknav(new TreeSet<>(ORIGINAL)), ORIGINAL }
).iterator();
}
@DataProvider(name="checkedSortedSet")
public Iterator<Object[]> checkedSortedSet() {
return Arrays.<Object[]>asList(
new Object[] { "ChkSorted", cksorted(new TreeSet<>(ORIGINAL)), ORIGINAL }
).iterator();
}
// mode bit tests for subList testing
boolean reverseList(int mode) { return (mode & 1) != 0; }
boolean reverseSub(int mode) { return (mode & 2) != 0; }
boolean isReversed(int mode) { return reverseList(mode) ^ reverseSub(mode); }
List<String> applyMode(int mode, List<String> base) {
var list = reverseList(mode) ? base.reversed() : base;
var sub = list.subList(2, 5);
return reverseSub(mode) ? sub.reversed() : sub;
}
/**
* Generate cases for testing subLists. For each different List implementation, generate 4
* cases from the two bits of the testing mode int value:
*
* (bit 1) if true, the List is reversed
* (bit 2) if true, the subList is reversed
*
* @return the generated cases
*/
@DataProvider(name="subListMods")
public Iterator<Object[]> subListMods() {
var cases = new ArrayList<Object[]>();
for (int mode = 0; mode < 4; mode++) {
cases.addAll(Arrays.asList(
new Object[] { "ArrayList", mode, new ArrayList<>(ORIGINAL), ORIGINAL },
new Object[] { "COWAL", mode, new CopyOnWriteArrayList<>(ORIGINAL), ORIGINAL },
new Object[] { "LinkedList", mode, new LinkedList<>(ORIGINAL), ORIGINAL },
new Object[] { "SimpleList", mode, new SimpleList<>(ORIGINAL), ORIGINAL }
));
}
return cases.iterator();
}
@DataProvider(name="iteratorMods")
public Iterator<Object[]> iteratorMods() {
var cases = new ArrayList<Object[]>();
for (boolean rev : List.of(false, true)) {
cases.addAll(Arrays.asList(
new Object[] { "ArrayList", rev, new ArrayList<>(ORIGINAL), ORIGINAL },
new Object[] { "LinkedList", rev, new LinkedList<>(ORIGINAL), ORIGINAL },
new Object[] { "SimpleList", rev, new SimpleList<>(ORIGINAL), ORIGINAL }
));
}
return cases.iterator();
}
@DataProvider(name="subListIteratorMods")
public Iterator<Object[]> subListIteratorMods() {
var cases = new ArrayList<Object[]>();
for (int mode = 0; mode < 4; mode++) {
cases.addAll(Arrays.asList(
new Object[] { "ArrayList", mode, new ArrayList<>(ORIGINAL), ORIGINAL },
new Object[] { "LinkedList", mode, new LinkedList<>(ORIGINAL), ORIGINAL },
new Object[] { "SimpleList", mode, new SimpleList<>(ORIGINAL), ORIGINAL }
));
}
return cases.iterator();
}
// ========== Assertions ==========
/**
* Basic checks over the contents of a SequencedCollection,
* compared to a reference List, in one direction.
*
* @param seq the SequencedCollection under test
* @param ref the reference List
*/
public void checkContents1(SequencedCollection<String> seq, List<String> ref) {
var list1 = new ArrayList<String>();
for (var s : seq)
list1.add(s);
assertEquals(list1, ref);
var list2 = new ArrayList<String>();
seq.forEach(list2::add);
assertEquals(list2, ref);
var list3 = Arrays.asList(seq.toArray());
assertEquals(list3, ref);
var list4 = Arrays.asList(seq.toArray(new String[0]));
assertEquals(list4, ref);
var list5 = Arrays.asList(seq.toArray(String[]::new));
assertEquals(list5, ref);
var list6 = seq.stream().toList();
assertEquals(list6, ref);
var list7 = seq.parallelStream().toList();
assertEquals(list7, ref);
assertEquals(seq.size(), ref.size());
assertEquals(seq.isEmpty(), ref.isEmpty());
for (var s : ref) {
assertTrue(seq.contains(s));
}
}
/**
* Check the contents of a SequencedCollection against a reference List,
* in both directions.
*
* @param seq the SequencedCollection under test
* @param ref the reference List
*/
public void checkContents(SequencedCollection<String> seq, List<String> ref) {
checkContents1(seq, ref);
var rref = copyReversed(ref);
var rseq = seq.reversed();
checkContents1(rseq, rref);
var rrseq = rseq.reversed();
checkContents1(rrseq, ref);
}
/**
* Check that modification operations will throw UnsupportedOperationException,
* in one direction.
*
* @param seq the SequencedCollection under test
*/
public void checkUnmodifiable1(SequencedCollection<String> seq) {
final var UOE = UnsupportedOperationException.class;
assertThrows(UOE, () -> seq.add("x"));
assertThrows(UOE, () -> seq.clear());
assertThrows(UOE, () -> { var it = seq.iterator(); it.next(); it.remove(); });
assertThrows(UOE, () -> seq.removeIf(x -> true));
assertThrows(UOE, () -> seq.addFirst("x"));
assertThrows(UOE, () -> seq.addLast("x"));
assertThrows(UOE, () -> seq.removeFirst());
assertThrows(UOE, () -> seq.removeLast());
// TODO these ops should throw unconditionally, but they don't in some implementations
// assertThrows(UOE, () -> seq.addAll(List.of()));
// assertThrows(UOE, () -> seq.remove("x"));
// assertThrows(UOE, () -> seq.removeAll(List.of()));
// assertThrows(UOE, () -> seq.removeIf(x -> false));
// assertThrows(UOE, () -> seq.retainAll(seq));
assertThrows(UOE, () -> seq.addAll(seq));
assertThrows(UOE, () -> seq.remove(seq.iterator().next()));
assertThrows(UOE, () -> seq.removeAll(seq));
assertThrows(UOE, () -> seq.retainAll(List.of()));
}
/**
* Check that modification operations will throw UnsupportedOperationException,
* in both directions.
*
* @param seq the SequencedCollection under test
*/
public void checkUnmodifiable(SequencedCollection<String> seq) {
checkUnmodifiable1(seq);
checkUnmodifiable1(seq.reversed());
}
static final Class<? extends Throwable> CCE = ClassCastException.class;
public void checkCheckedList(List<String> list) {
List<Object> objList = (List<Object>)(List)list;
assertThrows(CCE, () -> { objList.addFirst(new Object()); });
assertThrows(CCE, () -> { objList.addLast(new Object()); });
assertThrows(CCE, () -> { objList.reversed().addFirst(new Object()); });
assertThrows(CCE, () -> { objList.reversed().addLast(new Object()); });
}
public void checkCheckedNavSet(NavigableSet<String> set) {
NavigableSet<Object> objSet = (NavigableSet<Object>)(NavigableSet)set;
assertThrows(CCE, () -> { objSet.add(new Object()); });
assertThrows(CCE, () -> { objSet.reversed().add(new Object()); });
}
public void checkCheckedSortedSet(SortedSet<String> set) {
SortedSet<Object> objSet = (SortedSet<Object>)(SortedSet)set;
assertThrows(CCE, () -> { objSet.add(new Object()); });
assertThrows(CCE, () -> { objSet.reversed().add(new Object()); });
}
// ========== Tests ==========
@Test(dataProvider="all")
public void testFundamentals(String label, SequencedCollection<String> seq, List<String> ref) {
checkContents(seq, ref);
}
@Test(dataProvider="populated")
public void testGetFirst(String label, SequencedCollection<String> seq, List<String> ref) {
assertEquals(seq.getFirst(), ref.get(0));
assertEquals(seq.reversed().getFirst(), ref.get(ref.size() - 1));
checkContents(seq, ref);
}
@Test(dataProvider="populated")
public void testGetLast(String label, SequencedCollection<String> seq, List<String> ref) {
assertEquals(seq.getLast(), ref.get(ref.size() - 1));
assertEquals(seq.reversed().getLast(), ref.get(0));
checkContents(seq, ref);
}
@Test(dataProvider="empties")
public void testEmptyGetFirst(String label, SequencedCollection<String> seq, List<String> ref) {
assertThrows(NoSuchElementException.class, () -> seq.getFirst());
assertThrows(NoSuchElementException.class, () -> seq.reversed().getFirst());
checkContents(seq, ref);
}
@Test(dataProvider="empties")
public void testEmptyGetLast(String label, SequencedCollection<String> seq, List<String> ref) {
assertThrows(NoSuchElementException.class, () -> seq.getLast());
assertThrows(NoSuchElementException.class, () -> seq.reversed().getLast());
checkContents(seq, ref);
}
@Test(dataProvider="adds")
public void testAddFirst(String label, SequencedCollection<String> seq, List<String> baseref) {
var ref = new ArrayList<>(baseref);
ref.add(0, "x");
seq.addFirst("x");
checkContents(seq, ref);
}
@Test(dataProvider="adds")
public void testAddFirstRev(String label, SequencedCollection<String> seq, List<String> baseref) {
var ref = new ArrayList<>(baseref);
ref.add("x");
seq.reversed().addFirst("x");
checkContents(seq, ref);
}
@Test(dataProvider="adds")
public void testAddLast(String label, SequencedCollection<String> seq, List<String> baseref) {
var ref = new ArrayList<>(baseref);
ref.add("x");
seq.addLast("x");
checkContents(seq, ref);
}
@Test(dataProvider="adds")
public void testAddLastRev(String label, SequencedCollection<String> seq, List<String> baseref) {
var ref = new ArrayList<>(baseref);
ref.add(0, "x");
seq.reversed().addLast("x");
checkContents(seq, ref);
}
@Test(dataProvider="unpositionedAdd")
public void testUnpositionedAdd(String label, SequencedCollection<String> seq, List<String> baseref) {
var ref = new ArrayList<>(baseref);
ref.add("x");
seq.add("x");
checkContents(seq, ref);
}
@Test(dataProvider="unpositionedAdd")
public void testUnpositionedAddRev(String label, SequencedCollection<String> seq, List<String> baseref) {
var ref = new ArrayList<>(baseref);
ref.add("x");
seq.reversed().add("x");
checkContents(seq, ref);
}
@Test(dataProvider="removes")
public void testRemoveFirst(String label, SequencedCollection<String> seq, List<String> baseref) {
var ref = new ArrayList<>(baseref);
var exp = ref.remove(0);
var act = seq.removeFirst();
assertEquals(act, exp);
checkContents(seq, ref);
}
@Test(dataProvider="removes")
public void testRemoveFirstRev(String label, SequencedCollection<String> seq, List<String> baseref) {
var ref = new ArrayList<>(baseref);
var exp = ref.remove(ref.size() - 1);
var act = seq.reversed().removeFirst();
assertEquals(act, exp);
checkContents(seq, ref);
}
@Test(dataProvider="removes")
public void testRemoveLast(String label, SequencedCollection<String> seq, List<String> baseref) {
var ref = new ArrayList<>(baseref);
var exp = ref.remove(ref.size() - 1);
var act = seq.removeLast();
assertEquals(act, exp);
checkContents(seq, ref);
}
@Test(dataProvider="removes")
public void testRemoveLastRev(String label, SequencedCollection<String> seq, List<String> baseref) {
var ref = new ArrayList<>(baseref);
var exp = ref.remove(0);
var act = seq.reversed().removeLast();
assertEquals(act, exp);
checkContents(seq, ref);
}
@Test(dataProvider="emptyRemoves")
public void testEmptyRemoveFirst(String label, SequencedCollection<String> seq, List<String> baseref) {
assertThrows(NoSuchElementException.class, () -> seq.removeFirst());
assertThrows(NoSuchElementException.class, () -> seq.reversed().removeFirst());
checkContents(seq, baseref);
}
@Test(dataProvider="emptyRemoves")
public void testEmptyRemoveLast(String label, SequencedCollection<String> seq, List<String> baseref) {
assertThrows(NoSuchElementException.class, () -> seq.removeLast());
assertThrows(NoSuchElementException.class, () -> seq.reversed().removeLast());
checkContents(seq, baseref);
}
@Test(dataProvider="serializable")
public void testSerializable(String label, SequencedCollection<String> seq, List<String> ref)
throws ClassNotFoundException, IOException
{
var baos = new ByteArrayOutputStream();
try (var oos = new ObjectOutputStream(baos)) {
oos.writeObject(seq);
}
try (var bais = new ByteArrayInputStream(baos.toByteArray());
var ois = new ObjectInputStream(bais)) {
var seq2 = (SequencedCollection<String>) ois.readObject();
checkContents(seq2, ref);
}
}
@Test(dataProvider="notSerializable")
public void testNotSerializable(String label, SequencedCollection<String> seq)
throws ClassNotFoundException, IOException
{
var baos = new ByteArrayOutputStream();
try (var oos = new ObjectOutputStream(baos)) {
assertThrows(ObjectStreamException.class, () -> oos.writeObject(seq));
}
}
@Test(dataProvider="doubleReverse")
public void testDoubleReverse(String label, SequencedCollection<String> seq) {
var rrseq = seq.reversed().reversed();
assertSame(rrseq, seq);
}
@Test(dataProvider="unmodifiable")
public void testUnmodifiable(String label, SequencedCollection<String> seq, List<String> ref) {
checkUnmodifiable(seq);
checkContents(seq, ref);
}
@Test(dataProvider="checkedList")
public void testCheckedList(String label, List<String> list, List<String> ref) {
checkCheckedList(list);
checkContents(list, ref);
}
@Test(dataProvider="checkedNavSet")
public void testCheckedNavSet(String label, NavigableSet<String> set, List<String> ref) {
checkCheckedNavSet(set);
checkContents(set, ref);
}
@Test(dataProvider="checkedSortedSet")
public void testCheckedSortedSet(String label, SortedSet<String> set, List<String> ref) {
checkCheckedSortedSet(set);
checkContents(set, ref);
}
// Indexes for subList modification tests:
// 0 1 2 3 4 5 6
// a, b, c, d, e, f, g
// c, d, e
@Test(dataProvider="subListMods")
public void testSubListGet(String label, int mode, List<String> list, List<String> base) {
var sub = applyMode(mode, list);
assertEquals(sub.getFirst(), isReversed(mode) ? "e" : "c");
assertEquals(sub.getLast(), isReversed(mode) ? "c" : "e");
}
@Test(dataProvider="subListMods")
public void testSubListAddFirst(String label, int mode, List<String> list, List<String> base) {
var refList = new ArrayList<>(base);
var sub = applyMode(mode, list);
var refSub = new ArrayList<>(sub);
refList.add(isReversed(mode) ? 5 : 2, "x");
sub.addFirst("x");
refSub.add(0, "x");
checkContents(sub, refSub);
checkContents(list, refList);
}
@Test(dataProvider="subListMods")
public void testSubListAddLast(String label, int mode, List<String> list, List<String> base) {
var refList = new ArrayList<>(base);
var sub = applyMode(mode, list);
var refSub = new ArrayList<>(sub);
refList.add(isReversed(mode) ? 2 : 5, "x");
sub.addLast("x");
refSub.add("x");
checkContents(sub, refSub);
checkContents(list, refList);
}
@Test(dataProvider="subListMods")
public void testSubListRemoveFirst(String label, int mode, List<String> list, List<String> base) {
var refList = new ArrayList<>(base);
var sub = applyMode(mode, list);
var refSub = new ArrayList<>(sub);
refList.remove(isReversed(mode) ? 4 : 2);
var act = sub.removeFirst();
var exp = refSub.remove(0);
assertEquals(act, exp);
checkContents(sub, refSub);
checkContents(list, refList);
}
@Test(dataProvider="subListMods")
public void testSubListRemoveLast(String label, int mode, List<String> list, List<String> base) {
var refList = new ArrayList<>(base);
var sub = applyMode(mode, list);
var refSub = new ArrayList<>(sub);
refList.remove(isReversed(mode) ? 2 : 4);
var act = sub.removeLast();
var exp = refSub.remove(refSub.size() - 1);
assertEquals(act, exp);
checkContents(sub, refSub);
checkContents(list, refList);
}
@Test(dataProvider="subListMods")
public void testSubListAddAllFirst(String label, int mode, List<String> list, List<String> base) {
var refList = new ArrayList<>(base);
var sub = applyMode(mode, list);
var refSub = new ArrayList<>(sub);
if (isReversed(mode))
refList.addAll(5, List.of("y", "x"));
else
refList.addAll(2, List.of("x", "y"));
sub.addAll(0, List.of("x", "y"));
refSub.addAll(0, List.of("x", "y"));
checkContents(sub, refSub);
checkContents(list, refList);
}
@Test(dataProvider="subListMods")
public void testSubListAddAllLast(String label, int mode, List<String> list, List<String> base) {
var refList = new ArrayList<>(base);
var sub = applyMode(mode, list);
var refSub = new ArrayList<>(sub);
if (isReversed(mode))
refList.addAll(2, List.of("y", "x"));
else
refList.addAll(5, List.of("x", "y"));
sub.addAll(List.of("x", "y"));
refSub.addAll(List.of("x", "y"));
checkContents(sub, refSub);
checkContents(list, refList);
}
@Test(dataProvider="iteratorMods")
public void testListIteratorAdd(String label, boolean rev, List<String> list, List<String> base) {
var ref = new ArrayList<>(base);
var it = (rev ? list.reversed() : list).listIterator();
ref.add(rev ? 5 : 2, "x");
it.next();
it.next();
it.add("x");
assertEquals(it.next(), rev ? "e" : "c");
checkContents(list, ref);
}
@Test(dataProvider="iteratorMods")
public void testListIteratorSet(String label, boolean rev, List<String> list, List<String> base) {
var ref = new ArrayList<>(base);
var it = (rev ? list.reversed() : list).listIterator();
ref.set(rev ? 5 : 1, "x");
it.next();
it.next();
it.set("x");
assertEquals(it.next(), rev ? "e" : "c");
checkContents(list, ref);
}
@Test(dataProvider="iteratorMods")
public void testListIteratorRemove(String label, boolean rev, List<String> list, List<String> base) {
var ref = new ArrayList<>(base);
var it = (rev ? list.reversed() : list).listIterator();
ref.remove(rev ? 5 : 1);
it.next();
it.next();
it.remove();
assertEquals(it.next(), rev ? "e" : "c");
checkContents(list, ref);
}
// SubList ListIterator modification tests.
@Test(dataProvider="subListIteratorMods")
public void testSubListIteratorAdd(String label, int mode, List<String> list, List<String> base) {
var refList = new ArrayList<>(base);
var sub = applyMode(mode, list);
var refSub = new ArrayList<>(sub);
var it = sub.listIterator();
it.next();
it.add("x");
refList.add(isReversed(mode) ? 4 : 3, "x");
refSub.add(1, "x");
assertEquals(it.next(), "d");
checkContents(sub, refSub);
checkContents(list, refList);
}
@Test(dataProvider="subListIteratorMods")
public void testSubListIteratorSet(String label, int mode, List<String> list, List<String> base) {
var refList = new ArrayList<>(base);
var sub = applyMode(mode, list);
var refSub = new ArrayList<>(sub);
var it = sub.listIterator();
it.next();
it.set("x");
refList.set(isReversed(mode) ? 4 : 2, "x");
refSub.set(0, "x");
assertEquals(it.next(), "d");
checkContents(sub, refSub);
checkContents(list, refList);
}
@Test(dataProvider="subListIteratorMods")
public void testSubListIteratorRemove(String label, int mode, List<String> list, List<String> base) {
var refList = new ArrayList<>(base);
var sub = applyMode(mode, list);
var refSub = new ArrayList<>(sub);
var it = sub.listIterator();
it.next();
it.remove();
refList.remove(isReversed(mode) ? 4 : 2);
refSub.remove(0);
assertEquals(it.next(), "d");
checkContents(sub, refSub);
checkContents(list, refList);
}
}

View File

@ -0,0 +1,892 @@
/*
* Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
import java.io.*;
import java.util.*;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertNull;
import static org.testng.Assert.assertThrows;
import static org.testng.Assert.assertFalse;
import static org.testng.Assert.assertSame;
import static org.testng.Assert.assertTrue;
/*
* @test
* @bug 8266571
* @summary Basic tests for SequencedMap
* @modules java.base/java.util:open
* @build SimpleSortedMap
* @run testng BasicMap
*/
public class BasicMap {
// ========== Data Providers ==========
static final Class<? extends Throwable> CCE = ClassCastException.class;
static final Class<? extends Throwable> NSEE = NoSuchElementException.class;
static final Class<? extends Throwable> UOE = UnsupportedOperationException.class;
static final List<Map.Entry<String, Integer>> ORIGINAL =
List.of(Map.entry("a", 1),
Map.entry("b", 2),
Map.entry("c", 3),
Map.entry("d", 4),
Map.entry("e", 5));
static <M extends SequencedMap<String, Integer>>
M load(M map, List<Map.Entry<String, Integer>> mappings) {
for (var e : mappings)
map.put(e.getKey(), e.getValue());
return map;
}
static NavigableMap<String, Integer> cknav(NavigableMap<String, Integer> map) {
return Collections.checkedNavigableMap(map, String.class, Integer.class);
}
static SortedMap<String, Integer> cksorted(SortedMap<String, Integer> map) {
return Collections.checkedSortedMap(map, String.class, Integer.class);
}
static SequencedMap<String, Integer> umap(SequencedMap<String, Integer> map) {
return Collections.unmodifiableSequencedMap(map);
}
static SortedMap<String, Integer> usorted(SortedMap<String, Integer> map) {
return Collections.unmodifiableSortedMap(map);
}
static NavigableMap<String, Integer> unav(NavigableMap<String, Integer> map) {
return Collections.unmodifiableNavigableMap(map);
}
@DataProvider(name="all")
public Iterator<Object[]> all() {
var result = new ArrayList<Object[]>();
populated().forEachRemaining(result::add);
empties().forEachRemaining(result::add);
return result.iterator();
}
@DataProvider(name="populated")
public Iterator<Object[]> populated() {
return Arrays.asList(
new Object[] { "LinkedHashMap", load(new LinkedHashMap<>(), ORIGINAL), ORIGINAL },
new Object[] { "SimpleSortedMap", load(new SimpleSortedMap<>(), ORIGINAL), ORIGINAL },
new Object[] { "TreeMap", load(new TreeMap<>(), ORIGINAL), ORIGINAL },
new Object[] { "UnmodMap", umap(load(new LinkedHashMap<>(), ORIGINAL)), ORIGINAL }
).iterator();
}
@DataProvider(name="empties")
public Iterator<Object[]> empties() {
return Arrays.asList(
new Object[] { "EmptyNavigableMap", Collections.emptyNavigableMap(), List.of() },
new Object[] { "EmptySortedMap", Collections.emptySortedMap(), List.of() },
new Object[] { "LinkedHashMap", new LinkedHashMap<>(), List.of() },
new Object[] { "SimpleSortedMap", new SimpleSortedMap<>(), List.of() },
new Object[] { "TreeMap", new TreeMap<>(), List.of() },
new Object[] { "UnmodMap", umap(new LinkedHashMap<>()), List.of() }
).iterator();
}
@DataProvider(name="polls")
public Iterator<Object[]> polls() {
return Arrays.asList(
new Object[] { "LinkedHashMap", load(new LinkedHashMap<>(), ORIGINAL), ORIGINAL },
new Object[] { "SimpleSortedMap", load(new SimpleSortedMap<>(), ORIGINAL), ORIGINAL },
new Object[] { "TreeMap", load(new TreeMap<>(), ORIGINAL), ORIGINAL }
).iterator();
}
@DataProvider(name="emptyPolls")
public Iterator<Object[]> emptyPolls() {
return Arrays.asList(
new Object[] { "LinkedHashMap", new LinkedHashMap<>(), List.of() },
new Object[] { "SimpleSortedMap", new SimpleSortedMap<>(), List.of() },
new Object[] { "TreeMap", new TreeMap<>(), List.of() }
).iterator();
}
@DataProvider(name="puts")
public Iterator<Object[]> puts() {
return Arrays.<Object[]>asList(
new Object[] { "LinkedHashMap", load(new LinkedHashMap<>(), ORIGINAL), ORIGINAL }
).iterator();
}
@DataProvider(name="putUnpositioned")
public Iterator<Object[]> putUnpositioned() {
return Arrays.asList(
new Object[] { "LinkedHashMap", false, load(new LinkedHashMap<>(), ORIGINAL), ORIGINAL },
new Object[] { "LinkedHashMap", true, load(new LinkedHashMap<>(), ORIGINAL), ORIGINAL }
).iterator();
}
@DataProvider(name="putThrows")
public Iterator<Object[]> putThrows() {
return Arrays.asList(
new Object[] { "SimpleSortedMap", load(new SimpleSortedMap<>(), ORIGINAL), ORIGINAL },
new Object[] { "TreeMap", load(new TreeMap<>(), ORIGINAL), ORIGINAL }
).iterator();
}
@DataProvider(name="serializable")
public Iterator<Object[]> serializable() {
return Arrays.asList(
new Object[] { "LinkedHashMap", load(new LinkedHashMap<>(), ORIGINAL), ORIGINAL },
new Object[] { "TreeMap", load(new TreeMap<>(), ORIGINAL), ORIGINAL },
new Object[] { "UnmodMap", umap(load(new LinkedHashMap<>(), ORIGINAL)), ORIGINAL }
).iterator();
}
@DataProvider(name="notSerializable")
public Iterator<Object[]> notSerializable() {
return Arrays.asList(
new Object[] { "LinkedHashMap", load(new LinkedHashMap<>(), ORIGINAL).reversed() },
new Object[] { "UnmodMap", umap(load(new LinkedHashMap<>(), ORIGINAL)).reversed() }
).iterator();
}
@DataProvider(name="doubleReverse")
public Iterator<Object[]> doubleReverse() {
return Arrays.<Object[]>asList(
new Object[] { "LinkedHashMap", load(new LinkedHashMap<>(), ORIGINAL) }
).iterator();
}
@DataProvider(name="unmodifiable")
public Iterator<Object[]> unmodifible() {
return Arrays.<Object[]>asList(
new Object[] { "UnmodMap", umap(load(new LinkedHashMap<>(), ORIGINAL)), ORIGINAL },
new Object[] { "UnmodNav", unav(load(new TreeMap<>(), ORIGINAL)), ORIGINAL },
new Object[] { "UnmodSorted", usorted(load(new TreeMap<>(), ORIGINAL)), ORIGINAL }
).iterator();
}
@DataProvider(name="checked")
public Iterator<Object[]> checked() {
return Arrays.<Object[]>asList(
new Object[] { "ChkNav", cknav(load(new TreeMap<>(), ORIGINAL)), ORIGINAL },
new Object[] { "ChkSorted", cksorted(load(new TreeMap<>(), ORIGINAL)), ORIGINAL }
).iterator();
}
// mode bit tests
boolean reverseMap(int mode) { return (mode & 1) != 0; }
boolean reverseView(int mode) { return (mode & 2) != 0; }
boolean callLast(int mode) { return (mode & 4) != 0; }
boolean refLast(int mode) { return reverseMap(mode) ^ reverseView(mode) ^ callLast(mode); }
/**
* Generate cases for testing the removeFirst and removeLast methods of map views. For each
* different map implementation, generate 8 cases from the three bits of the testing mode
* int value:
*
* (bit 1) if true, the backing map is reversed
* (bit 2) if true, the view is reversed
* (bit 4) if true, the last element of the view is to be removed, otherwise the first
*
* The three bits XORed together (by refLast(), above) indicate (if true) the last
* or (if false) the first element of the reference entry list is to be removed.
*
* @return the generated cases
*/
@DataProvider(name="viewRemoves")
public Iterator<Object[]> viewRemoves() {
var cases = new ArrayList<Object[]>();
for (int mode = 0; mode < 8; mode++) {
cases.addAll(Arrays.asList(
new Object[] { "LinkedHashMap", mode, load(new LinkedHashMap<>(), ORIGINAL), ORIGINAL },
new Object[] { "SimpleSortedMap", mode, load(new SimpleSortedMap<>(), ORIGINAL), ORIGINAL },
new Object[] { "TreeMap", mode, load(new TreeMap<>(), ORIGINAL), ORIGINAL }
));
}
return cases.iterator();
}
@DataProvider(name="emptyViewRemoves")
public Iterator<Object[]> emptyViewRemoves() {
var cases = new ArrayList<Object[]>();
for (int mode = 0; mode < 8; mode++) {
cases.addAll(Arrays.asList(
new Object[] { "LinkedHashMap", mode, new LinkedHashMap<>(), List.of() },
new Object[] { "SimpleSortedMap", mode, new SimpleSortedMap<>(), List.of() },
new Object[] { "TreeMap", mode, new TreeMap<>(), List.of() }
));
}
return cases.iterator();
}
@DataProvider(name="viewAddThrows")
public Iterator<Object[]> viewAddThrows() {
var cases = new ArrayList<Object[]>();
for (int mode = 0; mode < 8; mode++) {
cases.addAll(Arrays.asList(
new Object[] { "LinkedHashMap", mode, load(new LinkedHashMap<>(), ORIGINAL), ORIGINAL },
new Object[] { "SimpleSortedMap", mode, load(new SimpleSortedMap<>(), ORIGINAL), ORIGINAL },
new Object[] { "TreeMap", mode, load(new TreeMap<>(), ORIGINAL), ORIGINAL }
));
}
return cases.iterator();
}
// ========== Assertions ==========
/**
* Basic checks over the contents of a SequencedMap, compared to a reference List of entries,
* in one direction.
*
* @param map the SequencedMap under test
* @param ref the reference list of entries
*/
public void checkContents1(SequencedMap<String, Integer> map, List<Map.Entry<String, Integer>> ref) {
var list1 = new ArrayList<Map.Entry<String, Integer>>();
map.forEach((k, v) -> list1.add(Map.entry(k, v)));
assertEquals(list1, ref);
assertEquals(map.size(), ref.size());
assertEquals(map.isEmpty(), ref.isEmpty());
for (var e : ref) {
assertTrue(map.containsKey(e.getKey()));
assertTrue(map.containsValue(e.getValue()));
assertEquals(map.get(e.getKey()), e.getValue());
}
}
public void checkContents(SequencedMap<String, Integer> map, List<Map.Entry<String, Integer>> ref) {
checkContents1(map, ref);
var rref = new ArrayList<>(ref);
Collections.reverse(rref);
var rmap = map.reversed();
checkContents1(rmap, rref);
var rrmap = rmap.reversed();
checkContents1(rrmap, ref);
}
/**
* Check the entrySet, keySet, or values view of a SequencedMap in one direction. The view
* collection is ordered even though the collection type is not sequenced.
*
* @param <T> the element type of the view
* @param mapView the actual map view
* @param expElements list of the expected elements
*/
public <T> void checkView1(Collection<T> mapView, List<T> expElements) {
var list1 = new ArrayList<T>();
for (var k : mapView)
list1.add(k);
assertEquals(list1, expElements);
var list2 = new ArrayList<T>();
mapView.forEach(list2::add);
assertEquals(list2, expElements);
var list3 = Arrays.asList(mapView.toArray());
assertEquals(list3, expElements);
var list4 = Arrays.asList(mapView.toArray(new Object[0]));
assertEquals(list4, expElements);
var list5 = Arrays.asList(mapView.toArray(Object[]::new));
assertEquals(list5, expElements);
var list6 = mapView.stream().toList();
assertEquals(list6, expElements);
var list7 = mapView.parallelStream().toList();
assertEquals(list7, expElements);
assertEquals(mapView.size(), expElements.size());
assertEquals(mapView.isEmpty(), expElements.isEmpty());
for (var k : expElements) {
assertTrue(mapView.contains(k));
}
var it = mapView.iterator();
if (expElements.isEmpty()) {
assertFalse(it.hasNext());
} else {
assertTrue(it.hasNext());
assertEquals(it.next(), expElements.get(0));
}
}
/**
* Check the sequenced entrySet, keySet, or values view of a SequencedMap in one direction.
*
* @param <T> the element type of the view
* @param mapView the actual map view
* @param expElements list of the expected elements
*/
public <T> void checkSeqView1(SequencedCollection<T> mapView, List<T> expElements) {
checkView1(mapView, expElements);
if (expElements.isEmpty()) {
assertThrows(NoSuchElementException.class, () -> mapView.getFirst());
assertThrows(NoSuchElementException.class, () -> mapView.getLast());
} else {
assertEquals(mapView.getFirst(), expElements.get(0));
assertEquals(mapView.getLast(), expElements.get(expElements.size() - 1));
}
}
/**
* Check the keySet and sequencedKeySet views of a map. It's possible to unify this with
* the corresponding checks for values and entrySet views, but doing this adds a bunch
* of generics and method references that tend to obscure more than they help.
*
* @param map the SequencedMap under test
* @param refEntries expected contents of the map
*/
public void checkKeySet(SequencedMap<String, Integer> map, List<Map.Entry<String, Integer>> refEntries) {
List<String> refKeys = refEntries.stream().map(Map.Entry::getKey).toList();
List<String> rrefKeys = new ArrayList<>(refKeys);
Collections.reverse(rrefKeys);
SequencedMap<String, Integer> rmap = map.reversed();
checkView1(map.keySet(), refKeys);
checkSeqView1(map.sequencedKeySet(), refKeys);
checkSeqView1(map.sequencedKeySet().reversed(), rrefKeys);
checkView1(rmap.keySet(), rrefKeys);
checkSeqView1(rmap.sequencedKeySet(), rrefKeys);
checkSeqView1(rmap.sequencedKeySet().reversed(), refKeys);
checkView1(rmap.reversed().keySet(), refKeys);
checkSeqView1(rmap.reversed().sequencedKeySet(), refKeys);
checkSeqView1(rmap.reversed().sequencedKeySet().reversed(), rrefKeys);
}
/**
* Check the values and sequencedValues views of a map.
*
* @param map the SequencedMap under test
* @param refEntries expected contents of the map
*/
public void checkValues(SequencedMap<String, Integer> map, List<Map.Entry<String, Integer>> refEntries) {
List<Integer> refValues = refEntries.stream().map(Map.Entry::getValue).toList();
List<Integer> rrefValues = new ArrayList<>(refValues);
Collections.reverse(rrefValues);
SequencedMap<String, Integer> rmap = map.reversed();
checkView1(map.values(), refValues);
checkSeqView1(map.sequencedValues(), refValues);
checkSeqView1(map.sequencedValues().reversed(), rrefValues);
checkView1(rmap.values(), rrefValues);
checkSeqView1(rmap.sequencedValues(), rrefValues);
checkSeqView1(rmap.sequencedValues().reversed(), refValues);
checkView1(rmap.reversed().values(), refValues);
checkSeqView1(rmap.reversed().sequencedValues(), refValues);
checkSeqView1(rmap.reversed().sequencedValues().reversed(), rrefValues);
}
/**
* Check the entrySet and sequencedEntrySet views of a map.
*
* @param map the SequencedMap under test
* @param refEntries expected contents of the map
*/
public void checkEntrySet(SequencedMap<String, Integer> map, List<Map.Entry<String, Integer>> refEntries) {
List<Map.Entry<String, Integer>> rref = new ArrayList<>(refEntries);
Collections.reverse(rref);
SequencedMap<String, Integer> rmap = map.reversed();
checkView1(map.entrySet(), refEntries);
checkSeqView1(map.sequencedEntrySet(), refEntries);
checkSeqView1(map.sequencedEntrySet().reversed(), rref);
checkView1(rmap.entrySet(), rref);
checkSeqView1(rmap.sequencedEntrySet(), rref);
checkSeqView1(rmap.sequencedEntrySet().reversed(), refEntries);
checkView1(rmap.reversed().entrySet(), refEntries);
checkSeqView1(rmap.reversed().sequencedEntrySet(), refEntries);
checkSeqView1(rmap.reversed().sequencedEntrySet().reversed(), rref);
}
/**
* Test attempted modifications to unmodifiable map views. The only mutating operation
* map views can support is removal.
*
* @param <T> element type of the map view
* @param view the map view
*/
public <T> void checkUnmodifiableView(Collection<T> view) {
assertThrows(UOE, () -> view.clear());
assertThrows(UOE, () -> { var it = view.iterator(); it.next(); it.remove(); });
assertThrows(UOE, () -> { var t = view.iterator().next(); view.remove(t); });
// TODO these ops should throw unconditionally, but they don't in some implementations
// assertThrows(UOE, () -> view.removeAll(List.of()));
// assertThrows(UOE, () -> view.removeIf(x -> false));
// assertThrows(UOE, () -> view.retainAll(view));
assertThrows(UOE, () -> view.removeAll(view));
assertThrows(UOE, () -> view.removeIf(x -> true));
assertThrows(UOE, () -> view.retainAll(List.of()));
}
/**
* Test removal methods on unmodifiable sequenced map views.
*
* @param <T> element type of the map view
* @param view the map view
*/
public <T> void checkUnmodifiableSeqView(SequencedCollection<T> view) {
checkUnmodifiableView(view);
assertThrows(UOE, () -> view.removeFirst());
assertThrows(UOE, () -> view.removeLast());
var rview = view.reversed();
checkUnmodifiableView(rview);
assertThrows(UOE, () -> rview.removeFirst());
assertThrows(UOE, () -> rview.removeLast());
}
public void checkUnmodifiableEntry(SequencedMap<String, Integer> map) {
assertThrows(UOE, () -> { map.firstEntry().setValue(99); });
assertThrows(UOE, () -> { map.lastEntry().setValue(99); });
assertThrows(UOE, () -> { map.sequencedEntrySet().getFirst().setValue(99); });
assertThrows(UOE, () -> { map.sequencedEntrySet().getLast().setValue(99); });
assertThrows(UOE, () -> { map.sequencedEntrySet().reversed().getFirst().setValue(99); });
assertThrows(UOE, () -> { map.sequencedEntrySet().reversed().getLast().setValue(99); });
}
public void checkUnmodifiable1(SequencedMap<String, Integer> map) {
assertThrows(UOE, () -> map.putFirst("x", 99));
assertThrows(UOE, () -> map.putLast("x", 99));
assertThrows(UOE, () -> { map.pollFirstEntry(); });
assertThrows(UOE, () -> { map.pollLastEntry(); });
checkUnmodifiableEntry(map);
checkUnmodifiableView(map.keySet());
checkUnmodifiableView(map.values());
checkUnmodifiableView(map.entrySet());
checkUnmodifiableSeqView(map.sequencedKeySet());
checkUnmodifiableSeqView(map.sequencedValues());
checkUnmodifiableSeqView(map.sequencedEntrySet());
}
public void checkUnmodifiable(SequencedMap<String, Integer> map) {
checkUnmodifiable1(map);
checkUnmodifiable1(map.reversed());
}
// The putFirst/putLast operations aren't tested here, because the only instances of
// checked, sequenced maps are SortedMap and NavigableMap, which don't support them.
public void checkChecked(SequencedMap<String, Integer> map) {
SequencedMap<Object, Object> objMap = (SequencedMap<Object, Object>)(SequencedMap)map;
assertThrows(CCE, () -> { objMap.put(new Object(), 99); });
assertThrows(CCE, () -> { objMap.put("x", new Object()); });
assertThrows(CCE, () -> { objMap.sequencedEntrySet().getFirst().setValue(new Object()); });
assertThrows(CCE, () -> { objMap.sequencedEntrySet().reversed().getFirst().setValue(new Object()); });
assertThrows(CCE, () -> { objMap.reversed().put(new Object(), 99); });
assertThrows(CCE, () -> { objMap.reversed().put("x", new Object()); });
assertThrows(CCE, () -> { objMap.reversed().sequencedEntrySet().getFirst().setValue(new Object()); });
assertThrows(CCE, () -> { objMap.reversed().sequencedEntrySet().reversed().getFirst().setValue(new Object()); });
}
// ========== Tests ==========
@Test(dataProvider="all")
public void testFundamentals(String label, SequencedMap<String, Integer> map, List<Map.Entry<String, Integer>> ref) {
checkContents(map, ref);
checkEntrySet(map, ref);
checkKeySet(map, ref);
checkValues(map, ref);
}
@Test(dataProvider="populated")
public void testFirstEntry(String label, SequencedMap<String, Integer> map, List<Map.Entry<String, Integer>> ref) {
assertEquals(map.firstEntry(), ref.get(0));
assertEquals(map.reversed().firstEntry(), ref.get(ref.size() - 1));
assertThrows(UOE, () -> { map.firstEntry().setValue(99); });
assertThrows(UOE, () -> { map.reversed().firstEntry().setValue(99); });
checkContents(map, ref);
}
@Test(dataProvider="populated")
public void testLastEntry(String label, SequencedMap<String, Integer> map, List<Map.Entry<String, Integer>> ref) {
assertEquals(map.lastEntry(), ref.get(ref.size() - 1));
assertEquals(map.reversed().lastEntry(), ref.get(0));
assertThrows(UOE, () -> { map.lastEntry().setValue(99); });
assertThrows(UOE, () -> { map.reversed().lastEntry().setValue(99); });
checkContents(map, ref);
}
@Test(dataProvider="empties")
public void testEmptyFirstEntry(String label, SequencedMap<String, Integer> map, List<Map.Entry<String, Integer>> ref) {
assertNull(map.firstEntry());
assertNull(map.reversed().firstEntry());
checkContents(map, ref);
}
@Test(dataProvider="empties")
public void testEmptyLastEntry(String label, SequencedMap<String, Integer> map, List<Map.Entry<String, Integer>> ref) {
assertNull(map.lastEntry());
assertNull(map.reversed().lastEntry());
checkContents(map, ref);
}
@Test(dataProvider="puts")
public void testPutFirst(String label, SequencedMap<String, Integer> map, List<Map.Entry<String, Integer>> baseref) {
var ref = new ArrayList<>(baseref);
ref.add(0, Map.entry("x", 99));
map.putFirst("x", 99);
checkContents(map, ref);
}
@Test(dataProvider="puts")
public void testPutFirstRev(String label, SequencedMap<String, Integer> map, List<Map.Entry<String, Integer>> baseref) {
var ref = new ArrayList<>(baseref);
ref.add(Map.entry("x", 99));
map.reversed().putFirst("x", 99);
checkContents(map, ref);
}
@Test(dataProvider="puts")
public void testPutLast(String label, SequencedMap<String, Integer> map, List<Map.Entry<String, Integer>> baseref) {
var ref = new ArrayList<>(baseref);
ref.add(Map.entry("x", 99));
map.putLast("x", 99);
checkContents(map, ref);
}
@Test(dataProvider="puts")
public void testPutLastRev(String label, SequencedMap<String, Integer> map, List<Map.Entry<String, Integer>> baseref) {
var ref = new ArrayList<>(baseref);
ref.add(0, Map.entry("x", 99));
map.reversed().putLast("x", 99);
checkContents(map, ref);
}
@Test(dataProvider="putUnpositioned")
public void testUnposPut(String label, boolean rev, SequencedMap<String, Integer> map, List<Map.Entry<String, Integer>> baseref) {
var ref = new ArrayList<>(baseref);
ref.add(Map.entry("x", 99));
(rev ? map.reversed() : map).put("x", 99);
checkContents(map, ref);
}
@Test(dataProvider="putUnpositioned")
public void testUnposPutAll(String label, boolean rev, SequencedMap<String, Integer> map, List<Map.Entry<String, Integer>> baseref) {
var ref = new ArrayList<>(baseref);
ref.add(Map.entry("x", 99));
(rev ? map.reversed() : map).putAll(Map.of("x", 99));
checkContents(map, ref);
}
@Test(dataProvider="putUnpositioned")
public void testUnposPutIfAbsent(String label, boolean rev, SequencedMap<String, Integer> map, List<Map.Entry<String, Integer>> baseref) {
var ref = new ArrayList<>(baseref);
ref.add(Map.entry("x", 99));
(rev ? map.reversed() : map).putIfAbsent("x", 99);
checkContents(map, ref);
}
@Test(dataProvider="putUnpositioned")
public void testUnposCompute(String label, boolean rev, SequencedMap<String, Integer> map, List<Map.Entry<String, Integer>> baseref) {
var ref = new ArrayList<>(baseref);
ref.add(Map.entry("x", 99));
(rev ? map.reversed() : map).compute("x", (k, v) -> 99);
checkContents(map, ref);
}
@Test(dataProvider="putUnpositioned")
public void testUnposComputeIfAbsent(String label, boolean rev, SequencedMap<String, Integer> map, List<Map.Entry<String, Integer>> baseref) {
var ref = new ArrayList<>(baseref);
ref.add(Map.entry("x", 99));
(rev ? map.reversed() : map).computeIfAbsent("x", k -> 99);
checkContents(map, ref);
}
@Test(dataProvider="putUnpositioned")
public void testUnposMerge(String label, boolean rev, SequencedMap<String, Integer> map, List<Map.Entry<String, Integer>> baseref) {
var ref = new ArrayList<>(baseref);
ref.add(Map.entry("x", 99));
(rev ? map.reversed() : map).merge("x", 99, /*unused*/ (k, v) -> -1);
checkContents(map, ref);
}
@Test(dataProvider="putThrows")
public void testPutThrows(String label, SequencedMap<String, Integer> map, List<Map.Entry<String, Integer>> baseref) {
assertThrows(UOE, () -> map.putFirst("x", 99));
assertThrows(UOE, () -> map.putLast("x", 99));
assertThrows(UOE, () -> map.reversed().putFirst("x", 99));
assertThrows(UOE, () -> map.reversed().putLast("x", 99));
checkContents(map, baseref);
}
@Test(dataProvider="polls")
public void testPollFirst(String label, SequencedMap<String, Integer> map, List<Map.Entry<String, Integer>> baseref) {
var ref = new ArrayList<>(baseref);
var act = map.pollFirstEntry();
assertEquals(act, ref.remove(0));
assertThrows(UOE, () -> { act.setValue(99); });
checkContents(map, ref);
}
@Test(dataProvider="polls")
public void testPollFirstRev(String label, SequencedMap<String, Integer> map, List<Map.Entry<String, Integer>> baseref) {
var ref = new ArrayList<>(baseref);
var act = map.reversed().pollFirstEntry();
assertEquals(act, ref.remove(ref.size() - 1));
assertThrows(UOE, () -> { act.setValue(99); });
checkContents(map, ref);
}
@Test(dataProvider="polls")
public void testPollLast(String label, SequencedMap<String, Integer> map, List<Map.Entry<String, Integer>> baseref) {
var ref = new ArrayList<>(baseref);
var act = map.pollLastEntry();
assertEquals(act, ref.remove(ref.size() - 1));
assertThrows(UOE, () -> { act.setValue(99); });
checkContents(map, ref);
}
@Test(dataProvider="polls")
public void testPollLastRev(String label, SequencedMap<String, Integer> map, List<Map.Entry<String, Integer>> baseref) {
var ref = new ArrayList<>(baseref);
var act = map.reversed().pollLastEntry();
assertEquals(act, ref.remove(0));
assertThrows(UOE, () -> { act.setValue(99); });
checkContents(map, ref);
}
@Test(dataProvider="emptyPolls")
public void testEmptyPollFirst(String label, SequencedMap<String, Integer> map, List<Map.Entry<String, Integer>> ref) {
assertNull(map.pollFirstEntry());
assertNull(map.reversed().pollFirstEntry());
checkContents(map, ref);
}
@Test(dataProvider="emptyPolls")
public void testEmptyPollLast(String label, SequencedMap<String, Integer> map, List<Map.Entry<String, Integer>> ref) {
assertNull(map.pollLastEntry());
assertNull(map.reversed().pollLastEntry());
checkContents(map, ref);
}
@Test(dataProvider="serializable")
public void testSerializable(String label, SequencedMap<String, Integer> map, List<Map.Entry<String, Integer>> ref)
throws ClassNotFoundException, IOException
{
var baos = new ByteArrayOutputStream();
try (var oos = new ObjectOutputStream(baos)) {
oos.writeObject(map);
}
try (var bais = new ByteArrayInputStream(baos.toByteArray());
var ois = new ObjectInputStream(bais)) {
var map2 = (SequencedMap<String, Integer>) ois.readObject();
checkContents(map2, ref);
}
}
@Test(dataProvider="notSerializable")
public void testNotSerializable(String label, SequencedMap<String, Integer> map)
throws ClassNotFoundException, IOException
{
var baos = new ByteArrayOutputStream();
try (var oos = new ObjectOutputStream(baos)) {
assertThrows(ObjectStreamException.class, () -> oos.writeObject(map));
}
}
@Test(dataProvider="doubleReverse")
public void testDoubleReverse(String label, SequencedMap<String, Integer> map) {
var rrmap = map.reversed().reversed();
assertSame(rrmap, map);
}
@Test(dataProvider="unmodifiable")
public void testUnmodifiable(String label, SequencedMap<String, Integer> map, List<Map.Entry<String, Integer>> ref) {
checkUnmodifiable(map);
checkContents(map, ref);
}
@Test(dataProvider="checked")
public void testChecked(String label, SequencedMap<String, Integer> map, List<Map.Entry<String, Integer>> ref) {
checkChecked(map);
checkContents(map, ref);
}
/**
* Test that a removal from the sequenedKeySet view is properly reflected in the original
* backing map. The mode value indicates whether the backing map is reversed, whether the
* sequencedKeySet view is reversed, and whether the removeFirst or removeLast is called
* on the view. See the viewRemoves() dataProvider for details.
*
* @param label the implementation label
* @param mode reversed and first/last modes
* @param map the original map instance
* @param baseref reference contents of the original map
*/
@Test(dataProvider="viewRemoves")
public void testKeySetRemoves(String label,
int mode,
SequencedMap<String, Integer> map,
List<Map.Entry<String, Integer>> baseref) {
var ref = new ArrayList<>(baseref);
var exp = (refLast(mode) ? ref.remove(ref.size() - 1) : ref.remove(0)).getKey();
var tempmap = reverseMap(mode) ? map.reversed() : map;
var keySet = reverseView(mode) ? tempmap.sequencedKeySet().reversed() : tempmap.sequencedKeySet();
var act = callLast(mode) ? keySet.removeLast() : keySet.removeFirst();
assertEquals(act, exp);
checkContents(map, ref);
}
// As above, but for the sequencedValues view.
@Test(dataProvider="viewRemoves")
public void testValuesRemoves(String label,
int mode,
SequencedMap<String, Integer> map,
List<Map.Entry<String, Integer>> baseref) {
var ref = new ArrayList<>(baseref);
var exp = (refLast(mode) ? ref.remove(ref.size() - 1) : ref.remove(0)).getValue();
var tempmap = reverseMap(mode) ? map.reversed() : map;
var values = reverseView(mode) ? tempmap.sequencedValues().reversed() : tempmap.sequencedValues();
var act = callLast(mode) ? values.removeLast() : values.removeFirst();
assertEquals(act, exp);
checkContents(map, ref);
}
// As above, but for the sequencedEntrySet view.
@Test(dataProvider="viewRemoves")
public void testEntrySetRemoves(String label,
int mode,
SequencedMap<String, Integer> map,
List<Map.Entry<String, Integer>> baseref) {
var ref = new ArrayList<>(baseref);
var exp = refLast(mode) ? ref.remove(ref.size() - 1) : ref.remove(0);
var tempmap = reverseMap(mode) ? map.reversed() : map;
var entrySet = reverseView(mode) ? tempmap.sequencedEntrySet().reversed() : tempmap.sequencedEntrySet();
var act = callLast(mode) ? entrySet.removeLast() : entrySet.removeFirst();
assertEquals(act, exp);
checkContents(map, ref);
}
// As above, but for the sequencedKeySet of an empty map.
@Test(dataProvider="emptyViewRemoves")
public void testEmptyKeySetRemoves(String label,
int mode,
SequencedMap<String, Integer> map,
List<Map.Entry<String, Integer>> baseref) {
var tempmap = reverseMap(mode) ? map.reversed() : map;
var keySet = reverseView(mode) ? tempmap.sequencedKeySet().reversed() : tempmap.sequencedKeySet();
if (callLast(mode))
assertThrows(NSEE, () -> keySet.removeLast());
else
assertThrows(NSEE, () -> keySet.removeFirst());
checkContents(map, baseref);
}
// As above, but for the sequencedValues view.
@Test(dataProvider="emptyViewRemoves")
public void testEmptyValuesRemoves(String label,
int mode,
SequencedMap<String, Integer> map,
List<Map.Entry<String, Integer>> baseref) {
var tempmap = reverseMap(mode) ? map.reversed() : map;
var values = reverseView(mode) ? tempmap.sequencedValues().reversed() : tempmap.sequencedValues();
if (callLast(mode))
assertThrows(NSEE, () -> values.removeLast());
else
assertThrows(NSEE, () -> values.removeFirst());
checkContents(map, baseref);
}
// As above, but for the sequencedEntrySet view.
@Test(dataProvider="emptyViewRemoves")
public void testEmptyEntrySetRemoves(String label,
int mode,
SequencedMap<String, Integer> map,
List<Map.Entry<String, Integer>> baseref) {
var tempmap = reverseMap(mode) ? map.reversed() : map;
var entrySet = reverseView(mode) ? tempmap.sequencedEntrySet().reversed() : tempmap.sequencedEntrySet();
if (callLast(mode))
assertThrows(NSEE, () -> entrySet.removeLast());
else
assertThrows(NSEE, () -> entrySet.removeFirst());
checkContents(map, baseref);
}
// Test that addFirst/addLast on the sequencedKeySetView throw UnsupportedOperationException.
@Test(dataProvider="viewAddThrows")
public void testKeySetAddThrows(String label,
int mode,
SequencedMap<String, Integer> map,
List<Map.Entry<String, Integer>> baseref) {
var tempmap = reverseMap(mode) ? map.reversed() : map;
var keySet = reverseView(mode) ? tempmap.sequencedKeySet().reversed() : tempmap.sequencedKeySet();
if (callLast(mode))
assertThrows(UOE, () -> keySet.addLast("x"));
else
assertThrows(UOE, () -> keySet.addFirst("x"));
checkContents(map, baseref);
}
// As above, but for the sequencedValues view.
@Test(dataProvider="viewAddThrows")
public void testValuesAddThrows(String label,
int mode,
SequencedMap<String, Integer> map,
List<Map.Entry<String, Integer>> baseref) {
var tempmap = reverseMap(mode) ? map.reversed() : map;
var values = reverseView(mode) ? tempmap.sequencedValues().reversed() : tempmap.sequencedValues();
if (callLast(mode))
assertThrows(UOE, () -> values.addLast(99));
else
assertThrows(UOE, () -> values.addFirst(99));
checkContents(map, baseref);
}
// As above, but for the sequencedEntrySet view.
@Test(dataProvider="viewAddThrows")
public void testEntrySetAddThrows(String label,
int mode,
SequencedMap<String, Integer> map,
List<Map.Entry<String, Integer>> baseref) {
var tempmap = reverseMap(mode) ? map.reversed() : map;
var entrySet = reverseView(mode) ? tempmap.sequencedEntrySet().reversed() : tempmap.sequencedEntrySet();
if (callLast(mode))
assertThrows(UOE, () -> entrySet.addLast(Map.entry("x", 99)));
else
assertThrows(UOE, () -> entrySet.addFirst(Map.entry("x", 99)));
checkContents(map, baseref);
}
}

View File

@ -0,0 +1,200 @@
/*
* Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
import java.util.*;
/**
* A complete Deque implementation that inherits the reversed() method
* from SequencedCollection. Useful for testing ReverseOrderDequeView.
* Underlying implementation provided by ArrayDeque.
*/
public class SimpleDeque<E> implements Deque<E> {
final Deque<E> deque;
public SimpleDeque() {
deque = new ArrayDeque<>();
}
public SimpleDeque(Collection<? extends E> c) {
deque = new ArrayDeque<>(c);
}
// ========== Object ==========
public boolean equals(Object o) {
return deque.equals(o);
}
public int hashCode() {
return deque.hashCode();
}
public String toString() {
return deque.toString();
}
// ========== Collection ==========
public boolean add(E e) {
return deque.add(e);
}
public boolean addAll(Collection<? extends E> c) {
return deque.addAll(c);
}
public void clear() {
deque.clear();
}
public boolean contains(Object o) {
return deque.contains(o);
}
public boolean containsAll(Collection<?> c) {
return deque.containsAll(c);
}
public boolean isEmpty() {
return deque.isEmpty();
}
public Iterator<E> iterator() {
return deque.iterator();
}
public boolean remove(Object o) {
return deque.remove(o);
}
public boolean removeAll(Collection<?> c) {
return deque.removeAll(c);
}
public boolean retainAll(Collection<?> c) {
return deque.retainAll(c);
}
public int size() {
return deque.size();
}
public Object[] toArray() {
return deque.toArray();
}
public <T> T[] toArray(T[] a) {
return deque.toArray(a);
}
// ========== Deque ==========
public void addFirst(E e) {
deque.addFirst(e);
}
public void addLast(E e) {
deque.addLast(e);
}
public boolean offerFirst(E e) {
return deque.offerFirst(e);
}
public boolean offerLast(E e) {
return deque.offerLast(e);
}
public E removeFirst() {
return deque.removeFirst();
}
public E removeLast() {
return deque.removeLast();
}
public E pollFirst() {
return deque.pollFirst();
}
public E pollLast() {
return deque.pollLast();
}
public E getFirst() {
return deque.getFirst();
}
public E getLast() {
return deque.getLast();
}
public E peekFirst() {
return deque.peekFirst();
}
public E peekLast() {
return deque.peekLast();
}
public boolean removeFirstOccurrence(Object o) {
return deque.removeFirstOccurrence(o);
}
public boolean removeLastOccurrence(Object o) {
return deque.removeLastOccurrence(o);
}
public boolean offer(E e) {
return deque.offer(e);
}
public E remove() {
return deque.remove();
}
public E poll() {
return deque.poll();
}
public E element() {
return deque.element();
}
public E peek() {
return deque.peek();
}
public void push(E e) {
deque.push(e);
}
public E pop() {
return deque.pop();
}
public Iterator<E> descendingIterator() {
return deque.descendingIterator();
}
}

View File

@ -0,0 +1,152 @@
/*
* Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
import java.util.*;
/**
* A complete List implementation that inherits the reversed() method
* from List. Useful for testing ReverseOrderListView.
* Underlying implementation provided by ArrayList.
*/
public class SimpleList<E> implements List<E> {
final List<E> list;
public SimpleList() {
list = new ArrayList<>();
}
public SimpleList(Collection<? extends E> c) {
list = new ArrayList<>(c);
}
// ========== Object ==========
public boolean equals(Object o) {
return list.equals(o);
}
public int hashCode() {
return list.hashCode();
}
public String toString() {
return list.toString();
}
// ========== Collection ==========
public boolean add(E e) {
return list.add(e);
}
public boolean addAll(Collection<? extends E> c) {
return list.addAll(c);
}
public void clear() {
list.clear();
}
public boolean contains(Object o) {
return list.contains(o);
}
public boolean containsAll(Collection<?> c) {
return list.containsAll(c);
}
public boolean isEmpty() {
return list.isEmpty();
}
public Iterator<E> iterator() {
return list.iterator();
}
public boolean remove(Object o) {
return list.remove(o);
}
public boolean removeAll(Collection<?> c) {
return list.removeAll(c);
}
public boolean retainAll(Collection<?> c) {
return list.retainAll(c);
}
public int size() {
return list.size();
}
public Object[] toArray() {
return list.toArray();
}
public <T> T[] toArray(T[] a) {
return list.toArray(a);
}
// ========== List ==========
public void add(int index, E element) {
list.add(index, element);
}
public boolean addAll(int index, Collection<? extends E> c) {
return list.addAll(index, c);
}
public E get(int index) {
return list.get(index);
}
public int indexOf(Object o) {
return list.indexOf(o);
}
public int lastIndexOf(Object o) {
return list.lastIndexOf(o);
}
public ListIterator<E> listIterator() {
return list.listIterator();
}
public ListIterator<E> listIterator(int index) {
return list.listIterator(index);
}
public E remove(int index) {
return list.remove(index);
}
public E set(int index, E element) {
return list.set(index, element);
}
public List<E> subList(int fromIndex, int toIndex) {
return list.subList(fromIndex, toIndex);
}
}

View File

@ -0,0 +1,134 @@
/*
* Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
import java.util.*;
/**
* A SortedMap implementation that does not implement NavigableMap. Useful for
* testing ReverseOrderSortedMapView. Underlying implementation provided by TreeMap.
*/
public class SimpleSortedMap<K,V> implements SortedMap<K,V> {
final SortedMap<K,V> map;
public SimpleSortedMap() {
map = new TreeMap<>();
}
public SimpleSortedMap(Comparator<? super K> comparator) {
map = new TreeMap<>(comparator);
}
public SimpleSortedMap(Map<? extends K,? extends V> m) {
map = new TreeMap<>(m);
}
// ========== Object ==========
public boolean equals(Object o) {
return map.equals(o);
}
public int hashCode() {
return map.hashCode();
}
public String toString() {
return map.toString();
}
// ========== Map ==========
public void clear() {
map.clear();
}
public boolean containsKey(Object key) {
return map.containsKey(key);
}
public boolean containsValue(Object value) {
return map.containsValue(value);
}
public Set<Map.Entry<K,V>> entrySet() {
return map.entrySet();
}
public V get(Object key) {
return map.get(key);
}
public boolean isEmpty() {
return map.isEmpty();
}
public Set<K> keySet() {
return map.keySet();
}
public V put(K key, V value) {
return map.put(key, value);
}
public void putAll(Map<? extends K,? extends V> m) {
map.putAll(m);
}
public V remove(Object key) {
return map.remove(key);
}
public int size() {
return map.size();
}
public Collection<V> values() {
return map.values();
}
// ========== SortedMap ==========
public Comparator<? super K> comparator() {
return map.comparator();
}
public K firstKey() {
return map.firstKey();
}
public SortedMap<K,V> headMap(K toKey) {
return map.headMap(toKey);
}
public K lastKey() {
return map.lastKey();
}
public SortedMap<K,V> subMap(K fromKey, K toKey) {
return map.subMap(fromKey, toKey);
}
public SortedMap<K,V> tailMap(K fromKey) {
return map.tailMap(fromKey);
}
}

View File

@ -0,0 +1,139 @@
/*
* Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
import java.util.*;
/**
* A SortedSet implementation that does not implement NavigableSet. Useful for
* testing ReverseOrderSortedSetView. Underlying implementation provided by TreeSet.
*/
public class SimpleSortedSet<E> implements SortedSet<E> {
final SortedSet<E> set;
public SimpleSortedSet() {
set = new TreeSet<E>();
}
public SimpleSortedSet(Collection<? extends E> c) {
set = new TreeSet<>(c);
}
public SimpleSortedSet(Comparator<? super E> comparator) {
set = new TreeSet<>(comparator);
}
// ========== Object ==========
public boolean equals(Object o) {
return set.equals(o);
}
public int hashCode() {
return set.hashCode();
}
public String toString() {
return set.toString();
}
// ========== Collection ==========
public boolean add(E e) {
return set.add(e);
}
public boolean addAll(Collection<? extends E> c) {
return set.addAll(c);
}
public void clear() {
set.clear();
}
public boolean contains(Object o) {
return set.contains(o);
}
public boolean containsAll(Collection<?> c) {
return set.containsAll(c);
}
public boolean isEmpty() {
return set.isEmpty();
}
public Iterator<E> iterator() {
return set.iterator();
}
public boolean remove(Object o) {
return set.remove(o);
}
public boolean removeAll(Collection<?> c) {
return set.removeAll(c);
}
public boolean retainAll(Collection<?> c) {
return set.retainAll(c);
}
public int size() {
return set.size();
}
public Object[] toArray() {
return set.toArray();
}
public <T> T[] toArray(T[] a) {
return set.toArray(a);
}
// ========== SortedSet ==========
public Comparator<? super E> comparator() {
return set.comparator();
}
public E first() {
return set.first();
}
public SortedSet<E> headSet(E toElement) {
return set.headSet(toElement);
}
public E last() {
return set.last();
}
public SortedSet<E> subSet(E fromElement, E toElement) {
return set.subSet(fromElement, toElement);
}
public SortedSet<E> tailSet(E fromElement) {
return set.tailSet(fromElement);
}
}

View File

@ -0,0 +1,78 @@
/*
* Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* @test
* @bug 8266571
* @modules java.base/jdk.internal.util
* @run testng Reverse
* @summary Tests for ArraysSupport.reverse
*/
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertSame;
import java.util.Arrays;
import jdk.internal.util.ArraysSupport;
public class Reverse {
@DataProvider(name = "data")
public Object[][] data() {
return new Object[][][] {
// pairs of actual, expected
{
{ },
{ }
}, {
{ "a" },
{ "a" }
}, {
{ "a", "b" },
{ "b", "a" }
}, {
{ "a", "b", "c" },
{ "c", "b", "a" }
}, {
{ "a", "b", "c", "d" },
{ "d", "c", "b", "a" }
}, {
{ "a", "b", "c", "d", "e" },
{ "e", "d", "c", "b", "a" }
}
};
}
@Test(dataProvider = "data")
public void testReverse(Object[] actual, Object[] expected) {
Object[] r = ArraysSupport.reverse(actual);
assertSame(r, actual);
assertEquals(actual.length, expected.length);
for (int i = 0; i < actual.length; i++) {
assertEquals(actual[i], expected[i],
"mismatch: actual=" + Arrays.asList(actual) +
" expected=" + Arrays.asList(expected) + " at index " + i);
}
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2005, 2016, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2005, 2023, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -102,7 +102,7 @@ public class TestJavacTaskScanner extends ToolTester {
System.out.println("#allMembers: " + numAllMembers);
check(numTokens, "#Tokens", 1054);
check(numParseTypeElements, "#parseTypeElements", 158);
check(numParseTypeElements, "#parseTypeElements", 180);
check(numAllMembers, "#allMembers", 52);
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2012, 2015, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2012, 2023, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -70,7 +70,7 @@ public class BoundsTest {
};
private static final String[] Single_supers = {
"java.lang.Object",
"java.util.Collection"
"java.util.SequencedCollection"
};
private static final String NoBounds_name = "NoBoundsTest.java";