8266571: Sequenced Collections
Reviewed-by: alanb
This commit is contained in:
parent
bad6aa68e4
commit
17ce0976e4
@ -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); }
|
||||
}
|
||||
}
|
||||
|
@ -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">
|
||||
|
@ -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}
|
||||
*/
|
||||
|
@ -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 — if changes
|
||||
* are permitted — 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
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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());
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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.
|
||||
*
|
||||
|
@ -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
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
279
src/java.base/share/classes/java/util/ReverseOrderDequeView.java
Normal file
279
src/java.base/share/classes/java/util/ReverseOrderDequeView.java
Normal 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);
|
||||
}
|
||||
}
|
405
src/java.base/share/classes/java/util/ReverseOrderListView.java
Normal file
405
src/java.base/share/classes/java/util/ReverseOrderListView.java
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
203
src/java.base/share/classes/java/util/SequencedCollection.java
Normal file
203
src/java.base/share/classes/java/util/SequencedCollection.java
Normal 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;
|
||||
}
|
||||
}
|
331
src/java.base/share/classes/java/util/SequencedMap.java
Normal file
331
src/java.base/share/classes/java/util/SequencedMap.java
Normal 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();
|
||||
}
|
||||
}
|
58
src/java.base/share/classes/java/util/SequencedSet.java
Normal file
58
src/java.base/share/classes/java/util/SequencedSet.java
Normal 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();
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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.)
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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")
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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 \
|
||||
|
@ -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) {
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
857
test/jdk/java/util/SequencedCollection/Basic.java
Normal file
857
test/jdk/java/util/SequencedCollection/Basic.java
Normal 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);
|
||||
}
|
||||
}
|
892
test/jdk/java/util/SequencedCollection/BasicMap.java
Normal file
892
test/jdk/java/util/SequencedCollection/BasicMap.java
Normal 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);
|
||||
}
|
||||
}
|
200
test/jdk/java/util/SequencedCollection/SimpleDeque.java
Normal file
200
test/jdk/java/util/SequencedCollection/SimpleDeque.java
Normal 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();
|
||||
}
|
||||
}
|
152
test/jdk/java/util/SequencedCollection/SimpleList.java
Normal file
152
test/jdk/java/util/SequencedCollection/SimpleList.java
Normal 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);
|
||||
}
|
||||
}
|
134
test/jdk/java/util/SequencedCollection/SimpleSortedMap.java
Normal file
134
test/jdk/java/util/SequencedCollection/SimpleSortedMap.java
Normal 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);
|
||||
}
|
||||
}
|
139
test/jdk/java/util/SequencedCollection/SimpleSortedSet.java
Normal file
139
test/jdk/java/util/SequencedCollection/SimpleSortedSet.java
Normal 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);
|
||||
}
|
||||
}
|
78
test/jdk/jdk/internal/util/ArraysSupport/Reverse.java
Normal file
78
test/jdk/jdk/internal/util/ArraysSupport/Reverse.java
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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";
|
||||
|
Loading…
x
Reference in New Issue
Block a user