Skip to content

Commit

Permalink
8307840: SequencedMap view method specification and implementation ad…
Browse files Browse the repository at this point in the history
…justments

Reviewed-by: darcy, alanb
  • Loading branch information
Stuart Marks committed Jun 6, 2023
1 parent 74dc50b commit 9526190
Show file tree
Hide file tree
Showing 3 changed files with 110 additions and 49 deletions.
44 changes: 22 additions & 22 deletions src/java.base/share/classes/java/util/AbstractMap.java
Expand Up @@ -893,35 +893,35 @@ public String toString() {
* <p>
* Ideally this would be a private class within SequencedMap, but private
* classes aren't permitted within interfaces.
* <p>
* The non-sequenced map view is obtained by calling the abstract view()
* method for each operation.
*
* @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; }
abstract Collection<E> 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); }
public void clear() { view().clear(); }
public boolean contains(Object o) { return view().contains(o); }
public boolean containsAll(Collection<?> c) { return view().containsAll(c); }
public void forEach(Consumer<? super E> c) { view().forEach(c); }
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); }
public String toString() { return view().toString(); }
}
}
73 changes: 46 additions & 27 deletions src/java.base/share/classes/java/util/SequencedMap.java
Expand Up @@ -259,45 +259,56 @@ default V putLast(K k, V v) {
}

/**
* Returns a {@link SequencedSet} view of this map's keySet.
* Returns a {@code SequencedSet} view of this map's {@link #keySet 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.
* The implementation in this interface returns a {@code SequencedSet} instance
* that behaves as follows. Its {@link SequencedSet#add add} and {@link
* SequencedSet#addAll addAll} methods throw {@link UnsupportedOperationException}.
* Its {@link SequencedSet#reversed reversed} method returns the {@link
* #sequencedKeySet sequencedKeySet} view of the {@link #reversed reversed} view of
* this map. Each of its other methods calls the corresponding method of the {@link
* #keySet keySet} view of this map.
*
* @return a SequencedSet view of this map's keySet
* @return a {@code SequencedSet} view of this map's {@code keySet}
*/
default SequencedSet<K> sequencedKeySet() {
class SeqKeySet extends AbstractMap.ViewCollection<K> implements SequencedSet<K> {
SeqKeySet() {
super(SequencedMap.this.keySet());
Collection<K> view() {
return SequencedMap.this.keySet();
}
public SequencedSet<K> reversed() {
return SequencedMap.this.reversed().sequencedKeySet();
}
public boolean equals(Object other) {
return view().equals(other);
}
public int hashCode() {
return view().hashCode();
}
}
return new SeqKeySet();
}

/**
* Returns a {@link SequencedCollection} view of this map's values collection.
* Returns a {@code SequencedCollection} view of this map's {@link #values 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.
* The implementation in this interface returns a {@code SequencedCollection} instance
* that behaves as follows. Its {@link SequencedCollection#add add} and {@link
* SequencedCollection#addAll addAll} methods throw {@link UnsupportedOperationException}.
* Its {@link SequencedCollection#reversed reversed} method returns the {@link
* #sequencedValues sequencedValues} view of the {@link #reversed reversed} view of
* this map. Its {@link Object#equals equals} and {@link Object#hashCode hashCode} methods
* are inherited from {@link Object}. Each of its other methods calls the corresponding
* method of the {@link #values values} view of this map.
*
* @return a SequencedCollection view of this map's values collection
* @return a {@code SequencedCollection} view of this map's {@code values} collection
*/
default SequencedCollection<V> sequencedValues() {
class SeqValues extends AbstractMap.ViewCollection<V> implements SequencedCollection<V> {
SeqValues() {
super(SequencedMap.this.values());
Collection<V> view() {
return SequencedMap.this.values();
}
public SequencedCollection<V> reversed() {
return SequencedMap.this.reversed().sequencedValues();
Expand All @@ -307,26 +318,34 @@ public SequencedCollection<V> reversed() {
}

/**
* Returns a {@link SequencedSet} view of this map's entrySet.
* Returns a {@code SequencedSet} view of this map's {@link #entrySet 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.
* The implementation in this interface returns a {@code SequencedSet} instance
* that behaves as follows. Its {@link SequencedSet#add add} and {@link
* SequencedSet#addAll addAll} methods throw {@link UnsupportedOperationException}.
* Its {@link SequencedSet#reversed reversed} method returns the {@link
* #sequencedEntrySet sequencedEntrySet} view of the {@link #reversed reversed} view of
* this map. Each of its other methods calls the corresponding method of the {@link
* #entrySet entrySet} view of this map.
*
* @return a SequencedSet view of this map's entrySet
* @return a {@code SequencedSet} view of this map's {@code 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());
Collection<Map.Entry<K, V>> view() {
return SequencedMap.this.entrySet();
}
public SequencedSet<Map.Entry<K, V>> reversed() {
return SequencedMap.this.reversed().sequencedEntrySet();
}
public boolean equals(Object other) {
return view().equals(other);
}
public int hashCode() {
return view().hashCode();
}
}
return new SeqEntrySet();
}
Expand Down
42 changes: 42 additions & 0 deletions test/jdk/java/util/SequencedCollection/BasicMap.java
Expand Up @@ -397,6 +397,20 @@ public void checkKeySet(SequencedMap<String, Integer> map, List<Map.Entry<String
checkView1(rmap.reversed().keySet(), refKeys);
checkSeqView1(rmap.reversed().sequencedKeySet(), refKeys);
checkSeqView1(rmap.reversed().sequencedKeySet().reversed(), rrefKeys);

assertEquals(map.keySet().hashCode(), rmap.keySet().hashCode());
assertEquals(map.keySet().hashCode(), map.sequencedKeySet().hashCode());
assertEquals(rmap.keySet().hashCode(), rmap.sequencedKeySet().hashCode());

// Don't use assertEquals(), as we really want to test the equals() methods.
assertTrue(map.keySet().equals(map.sequencedKeySet()));
assertTrue(map.sequencedKeySet().equals(map.keySet()));
assertTrue(rmap.keySet().equals(map.sequencedKeySet()));
assertTrue(rmap.sequencedKeySet().equals(map.keySet()));
assertTrue(map.keySet().equals(rmap.sequencedKeySet()));
assertTrue(map.sequencedKeySet().equals(rmap.keySet()));
assertTrue(rmap.keySet().equals(rmap.sequencedKeySet()));
assertTrue(rmap.sequencedKeySet().equals(rmap.keySet()));
}

/**
Expand All @@ -422,6 +436,21 @@ public void checkValues(SequencedMap<String, Integer> map, List<Map.Entry<String
checkView1(rmap.reversed().values(), refValues);
checkSeqView1(rmap.reversed().sequencedValues(), refValues);
checkSeqView1(rmap.reversed().sequencedValues().reversed(), rrefValues);

// No assertions over hashCode(), as Collection inherits Object.hashCode
// which is usually but not guaranteed to give unequal results.

// It's permissible for an implementation to return the same instance for values()
// as for sequencedValues(). Either they're the same instance, or they must be
// unequal, because distinct collections should always be unequal.

var v = map.values();
var sv = map.sequencedValues();
assertTrue((v == sv) || ! (v.equals(sv) || sv.equals(v)));

var rv = rmap.values();
var rsv = rmap.sequencedValues();
assertTrue((rv == rsv) || ! (rv.equals(rsv) || rsv.equals(rv)));
}

/**
Expand All @@ -446,6 +475,19 @@ public void checkEntrySet(SequencedMap<String, Integer> map, List<Map.Entry<Stri
checkView1(rmap.reversed().entrySet(), refEntries);
checkSeqView1(rmap.reversed().sequencedEntrySet(), refEntries);
checkSeqView1(rmap.reversed().sequencedEntrySet().reversed(), rref);

assertEquals(map.entrySet().hashCode(), rmap.entrySet().hashCode());
assertEquals(map.entrySet().hashCode(), map.sequencedEntrySet().hashCode());
assertEquals(map.sequencedEntrySet().hashCode(), map.entrySet().hashCode());

assertTrue(map.entrySet().equals(map.sequencedEntrySet()));
assertTrue(map.sequencedEntrySet().equals(map.entrySet()));
assertTrue(rmap.entrySet().equals(map.sequencedEntrySet()));
assertTrue(rmap.sequencedEntrySet().equals(map.entrySet()));
assertTrue(map.entrySet().equals(rmap.sequencedEntrySet()));
assertTrue(map.sequencedEntrySet().equals(rmap.entrySet()));
assertTrue(rmap.entrySet().equals(rmap.sequencedEntrySet()));
assertTrue(rmap.sequencedEntrySet().equals(rmap.entrySet()));
}

/**
Expand Down

1 comment on commit 9526190

@openjdk-notifier
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.