mirror of
https://github.com/openjdk/jdk.git
synced 2025-08-27 14:54:52 +02:00
8266571: Sequenced Collections
Reviewed-by: alanb
This commit is contained in:
parent
bad6aa68e4
commit
17ce0976e4
42 changed files with 7060 additions and 150 deletions
|
@ -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")
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue