diff --git a/src/java.base/share/classes/java/util/AbstractMap.java b/src/java.base/share/classes/java/util/AbstractMap.java index b0d311f6565..7a5d92bda52 100644 --- a/src/java.base/share/classes/java/util/AbstractMap.java +++ b/src/java.base/share/classes/java/util/AbstractMap.java @@ -893,35 +893,35 @@ public abstract class AbstractMap implements Map { *

* Ideally this would be a private class within SequencedMap, but private * classes aren't permitted within interfaces. + *

+ * The non-sequenced map view is obtained by calling the abstract view() + * method for each operation. * * @param the view's element type */ /* non-public */ abstract static class ViewCollection implements Collection { UnsupportedOperationException uoe() { return new UnsupportedOperationException(); } - final Collection view; - - ViewCollection(Collection view) { this.view = view; } + abstract Collection view(); public boolean add(E t) { throw uoe(); } public boolean addAll(Collection 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 c) { view.forEach(c); } - public int hashCode() { return view.hashCode(); } - public boolean isEmpty() { return view.isEmpty(); } - public Iterator iterator() { return view.iterator(); } - public Stream 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 filter) { return view.removeIf(filter); } - public boolean retainAll(Collection c) { return view.retainAll(c); } - public int size() { return view.size(); } - public Spliterator spliterator() { return view.spliterator(); } - public Stream stream() { return view.stream(); } - public Object[] toArray() { return view.toArray(); } - public T[] toArray(IntFunction generator) { return view.toArray(generator); } - public 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 c) { view().forEach(c); } + public boolean isEmpty() { return view().isEmpty(); } + public Iterator iterator() { return view().iterator(); } + public Stream 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 filter) { return view().removeIf(filter); } + public boolean retainAll(Collection c) { return view().retainAll(c); } + public int size() { return view().size(); } + public Spliterator spliterator() { return view().spliterator(); } + public Stream stream() { return view().stream(); } + public Object[] toArray() { return view().toArray(); } + public T[] toArray(IntFunction generator) { return view().toArray(generator); } + public T[] toArray(T[] a) { return view().toArray(a); } + public String toString() { return view().toString(); } } } diff --git a/src/java.base/share/classes/java/util/SequencedMap.java b/src/java.base/share/classes/java/util/SequencedMap.java index aeab9501736..6bff204b65d 100644 --- a/src/java.base/share/classes/java/util/SequencedMap.java +++ b/src/java.base/share/classes/java/util/SequencedMap.java @@ -259,45 +259,56 @@ public interface SequencedMap extends Map { } /** - * 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 sequencedKeySet() { class SeqKeySet extends AbstractMap.ViewCollection implements SequencedSet { - SeqKeySet() { - super(SequencedMap.this.keySet()); + Collection view() { + return SequencedMap.this.keySet(); } public SequencedSet 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 sequencedValues() { class SeqValues extends AbstractMap.ViewCollection implements SequencedCollection { - SeqValues() { - super(SequencedMap.this.values()); + Collection view() { + return SequencedMap.this.values(); } public SequencedCollection reversed() { return SequencedMap.this.reversed().sequencedValues(); @@ -307,26 +318,34 @@ public interface SequencedMap extends Map { } /** - * 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> sequencedEntrySet() { class SeqEntrySet extends AbstractMap.ViewCollection> implements SequencedSet> { - SeqEntrySet() { - super(SequencedMap.this.entrySet()); + Collection> view() { + return SequencedMap.this.entrySet(); } public SequencedSet> reversed() { return SequencedMap.this.reversed().sequencedEntrySet(); } + public boolean equals(Object other) { + return view().equals(other); + } + public int hashCode() { + return view().hashCode(); + } } return new SeqEntrySet(); } diff --git a/test/jdk/java/util/SequencedCollection/BasicMap.java b/test/jdk/java/util/SequencedCollection/BasicMap.java index 6bcbba74d09..028ae4e42c2 100644 --- a/test/jdk/java/util/SequencedCollection/BasicMap.java +++ b/test/jdk/java/util/SequencedCollection/BasicMap.java @@ -397,6 +397,20 @@ public class BasicMap { 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())); } /** @@ -422,6 +436,21 @@ public class BasicMap { 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))); } /** @@ -446,6 +475,19 @@ public class BasicMap { 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())); } /**