Path: blob/master/src/java.base/share/classes/java/util/AbstractMap.java
41152 views
/*1* Copyright (c) 1997, 2019, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package java.util;26import java.util.Map.Entry;2728/**29* This class provides a skeletal implementation of the {@code Map}30* interface, to minimize the effort required to implement this interface.31*32* <p>To implement an unmodifiable map, the programmer needs only to extend this33* class and provide an implementation for the {@code entrySet} method, which34* returns a set-view of the map's mappings. Typically, the returned set35* will, in turn, be implemented atop {@code AbstractSet}. This set should36* not support the {@code add} or {@code remove} methods, and its iterator37* should not support the {@code remove} method.38*39* <p>To implement a modifiable map, the programmer must additionally override40* this class's {@code put} method (which otherwise throws an41* {@code UnsupportedOperationException}), and the iterator returned by42* {@code entrySet().iterator()} must additionally implement its43* {@code remove} method.44*45* <p>The programmer should generally provide a void (no argument) and map46* constructor, as per the recommendation in the {@code Map} interface47* specification.48*49* <p>The documentation for each non-abstract method in this class describes its50* implementation in detail. Each of these methods may be overridden if the51* map being implemented admits a more efficient implementation.52*53* <p>This class is a member of the54* <a href="{@docRoot}/java.base/java/util/package-summary.html#CollectionsFramework">55* Java Collections Framework</a>.56*57* @param <K> the type of keys maintained by this map58* @param <V> the type of mapped values59*60* @author Josh Bloch61* @author Neal Gafter62* @see Map63* @see Collection64* @since 1.265*/6667public abstract class AbstractMap<K,V> implements Map<K,V> {68/**69* Sole constructor. (For invocation by subclass constructors, typically70* implicit.)71*/72protected AbstractMap() {73}7475// Query Operations7677/**78* {@inheritDoc}79*80* @implSpec81* This implementation returns {@code entrySet().size()}.82*/83public int size() {84return entrySet().size();85}8687/**88* {@inheritDoc}89*90* @implSpec91* This implementation returns {@code size() == 0}.92*/93public boolean isEmpty() {94return size() == 0;95}9697/**98* {@inheritDoc}99*100* @implSpec101* This implementation iterates over {@code entrySet()} searching102* for an entry with the specified value. If such an entry is found,103* {@code true} is returned. If the iteration terminates without104* finding such an entry, {@code false} is returned. Note that this105* implementation requires linear time in the size of the map.106*107* @throws ClassCastException {@inheritDoc}108* @throws NullPointerException {@inheritDoc}109*/110public boolean containsValue(Object value) {111Iterator<Entry<K,V>> i = entrySet().iterator();112if (value==null) {113while (i.hasNext()) {114Entry<K,V> e = i.next();115if (e.getValue()==null)116return true;117}118} else {119while (i.hasNext()) {120Entry<K,V> e = i.next();121if (value.equals(e.getValue()))122return true;123}124}125return false;126}127128/**129* {@inheritDoc}130*131* @implSpec132* This implementation iterates over {@code entrySet()} searching133* for an entry with the specified key. If such an entry is found,134* {@code true} is returned. If the iteration terminates without135* finding such an entry, {@code false} is returned. Note that this136* implementation requires linear time in the size of the map; many137* implementations will override this method.138*139* @throws ClassCastException {@inheritDoc}140* @throws NullPointerException {@inheritDoc}141*/142public boolean containsKey(Object key) {143Iterator<Map.Entry<K,V>> i = entrySet().iterator();144if (key==null) {145while (i.hasNext()) {146Entry<K,V> e = i.next();147if (e.getKey()==null)148return true;149}150} else {151while (i.hasNext()) {152Entry<K,V> e = i.next();153if (key.equals(e.getKey()))154return true;155}156}157return false;158}159160/**161* {@inheritDoc}162*163* @implSpec164* This implementation iterates over {@code entrySet()} searching165* for an entry with the specified key. If such an entry is found,166* the entry's value is returned. If the iteration terminates without167* finding such an entry, {@code null} is returned. Note that this168* implementation requires linear time in the size of the map; many169* implementations will override this method.170*171* @throws ClassCastException {@inheritDoc}172* @throws NullPointerException {@inheritDoc}173*/174public V get(Object key) {175Iterator<Entry<K,V>> i = entrySet().iterator();176if (key==null) {177while (i.hasNext()) {178Entry<K,V> e = i.next();179if (e.getKey()==null)180return e.getValue();181}182} else {183while (i.hasNext()) {184Entry<K,V> e = i.next();185if (key.equals(e.getKey()))186return e.getValue();187}188}189return null;190}191192193// Modification Operations194195/**196* {@inheritDoc}197*198* @implSpec199* This implementation always throws an200* {@code UnsupportedOperationException}.201*202* @throws UnsupportedOperationException {@inheritDoc}203* @throws ClassCastException {@inheritDoc}204* @throws NullPointerException {@inheritDoc}205* @throws IllegalArgumentException {@inheritDoc}206*/207public V put(K key, V value) {208throw new UnsupportedOperationException();209}210211/**212* {@inheritDoc}213*214* @implSpec215* This implementation iterates over {@code entrySet()} searching for an216* entry with the specified key. If such an entry is found, its value is217* obtained with its {@code getValue} operation, the entry is removed218* from the collection (and the backing map) with the iterator's219* {@code remove} operation, and the saved value is returned. If the220* iteration terminates without finding such an entry, {@code null} is221* returned. Note that this implementation requires linear time in the222* size of the map; many implementations will override this method.223*224* <p>Note that this implementation throws an225* {@code UnsupportedOperationException} if the {@code entrySet}226* iterator does not support the {@code remove} method and this map227* contains a mapping for the specified key.228*229* @throws UnsupportedOperationException {@inheritDoc}230* @throws ClassCastException {@inheritDoc}231* @throws NullPointerException {@inheritDoc}232*/233public V remove(Object key) {234Iterator<Entry<K,V>> i = entrySet().iterator();235Entry<K,V> correctEntry = null;236if (key==null) {237while (correctEntry==null && i.hasNext()) {238Entry<K,V> e = i.next();239if (e.getKey()==null)240correctEntry = e;241}242} else {243while (correctEntry==null && i.hasNext()) {244Entry<K,V> e = i.next();245if (key.equals(e.getKey()))246correctEntry = e;247}248}249250V oldValue = null;251if (correctEntry !=null) {252oldValue = correctEntry.getValue();253i.remove();254}255return oldValue;256}257258259// Bulk Operations260261/**262* {@inheritDoc}263*264* @implSpec265* This implementation iterates over the specified map's266* {@code entrySet()} collection, and calls this map's {@code put}267* operation once for each entry returned by the iteration.268*269* <p>Note that this implementation throws an270* {@code UnsupportedOperationException} if this map does not support271* the {@code put} operation and the specified map is nonempty.272*273* @throws UnsupportedOperationException {@inheritDoc}274* @throws ClassCastException {@inheritDoc}275* @throws NullPointerException {@inheritDoc}276* @throws IllegalArgumentException {@inheritDoc}277*/278public void putAll(Map<? extends K, ? extends V> m) {279for (Map.Entry<? extends K, ? extends V> e : m.entrySet())280put(e.getKey(), e.getValue());281}282283/**284* {@inheritDoc}285*286* @implSpec287* This implementation calls {@code entrySet().clear()}.288*289* <p>Note that this implementation throws an290* {@code UnsupportedOperationException} if the {@code entrySet}291* does not support the {@code clear} operation.292*293* @throws UnsupportedOperationException {@inheritDoc}294*/295public void clear() {296entrySet().clear();297}298299300// Views301302/**303* Each of these fields are initialized to contain an instance of the304* appropriate view the first time this view is requested. The views are305* stateless, so there's no reason to create more than one of each.306*307* <p>Since there is no synchronization performed while accessing these fields,308* it is expected that java.util.Map view classes using these fields have309* no non-final fields (or any fields at all except for outer-this). Adhering310* to this rule would make the races on these fields benign.311*312* <p>It is also imperative that implementations read the field only once,313* as in:314*315* <pre> {@code316* public Set<K> keySet() {317* Set<K> ks = keySet; // single racy read318* if (ks == null) {319* ks = new KeySet();320* keySet = ks;321* }322* return ks;323* }324*}</pre>325*/326transient Set<K> keySet;327transient Collection<V> values;328329/**330* {@inheritDoc}331*332* @implSpec333* This implementation returns a set that subclasses {@link AbstractSet}.334* The subclass's iterator method returns a "wrapper object" over this335* map's {@code entrySet()} iterator. The {@code size} method336* delegates to this map's {@code size} method and the337* {@code contains} method delegates to this map's338* {@code containsKey} method.339*340* <p>The set is created the first time this method is called,341* and returned in response to all subsequent calls. No synchronization342* is performed, so there is a slight chance that multiple calls to this343* method will not all return the same set.344*/345public Set<K> keySet() {346Set<K> ks = keySet;347if (ks == null) {348ks = new AbstractSet<K>() {349public Iterator<K> iterator() {350return new Iterator<K>() {351private Iterator<Entry<K,V>> i = entrySet().iterator();352353public boolean hasNext() {354return i.hasNext();355}356357public K next() {358return i.next().getKey();359}360361public void remove() {362i.remove();363}364};365}366367public int size() {368return AbstractMap.this.size();369}370371public boolean isEmpty() {372return AbstractMap.this.isEmpty();373}374375public void clear() {376AbstractMap.this.clear();377}378379public boolean contains(Object k) {380return AbstractMap.this.containsKey(k);381}382};383keySet = ks;384}385return ks;386}387388/**389* {@inheritDoc}390*391* @implSpec392* This implementation returns a collection that subclasses {@link393* AbstractCollection}. The subclass's iterator method returns a394* "wrapper object" over this map's {@code entrySet()} iterator.395* The {@code size} method delegates to this map's {@code size}396* method and the {@code contains} method delegates to this map's397* {@code containsValue} method.398*399* <p>The collection is created the first time this method is called, and400* returned in response to all subsequent calls. No synchronization is401* performed, so there is a slight chance that multiple calls to this402* method will not all return the same collection.403*/404public Collection<V> values() {405Collection<V> vals = values;406if (vals == null) {407vals = new AbstractCollection<V>() {408public Iterator<V> iterator() {409return new Iterator<V>() {410private Iterator<Entry<K,V>> i = entrySet().iterator();411412public boolean hasNext() {413return i.hasNext();414}415416public V next() {417return i.next().getValue();418}419420public void remove() {421i.remove();422}423};424}425426public int size() {427return AbstractMap.this.size();428}429430public boolean isEmpty() {431return AbstractMap.this.isEmpty();432}433434public void clear() {435AbstractMap.this.clear();436}437438public boolean contains(Object v) {439return AbstractMap.this.containsValue(v);440}441};442values = vals;443}444return vals;445}446447public abstract Set<Entry<K,V>> entrySet();448449450// Comparison and hashing451452/**453* Compares the specified object with this map for equality. Returns454* {@code true} if the given object is also a map and the two maps455* represent the same mappings. More formally, two maps {@code m1} and456* {@code m2} represent the same mappings if457* {@code m1.entrySet().equals(m2.entrySet())}. This ensures that the458* {@code equals} method works properly across different implementations459* of the {@code Map} interface.460*461* @implSpec462* This implementation first checks if the specified object is this map;463* if so it returns {@code true}. Then, it checks if the specified464* object is a map whose size is identical to the size of this map; if465* not, it returns {@code false}. If so, it iterates over this map's466* {@code entrySet} collection, and checks that the specified map467* contains each mapping that this map contains. If the specified map468* fails to contain such a mapping, {@code false} is returned. If the469* iteration completes, {@code true} is returned.470*471* @param o object to be compared for equality with this map472* @return {@code true} if the specified object is equal to this map473*/474public boolean equals(Object o) {475if (o == this)476return true;477478if (!(o instanceof Map<?, ?> m))479return false;480if (m.size() != size())481return false;482483try {484for (Entry<K, V> e : entrySet()) {485K key = e.getKey();486V value = e.getValue();487if (value == null) {488if (!(m.get(key) == null && m.containsKey(key)))489return false;490} else {491if (!value.equals(m.get(key)))492return false;493}494}495} catch (ClassCastException unused) {496return false;497} catch (NullPointerException unused) {498return false;499}500501return true;502}503504/**505* Returns the hash code value for this map. The hash code of a map is506* defined to be the sum of the hash codes of each entry in the map's507* {@code entrySet()} view. This ensures that {@code m1.equals(m2)}508* implies that {@code m1.hashCode()==m2.hashCode()} for any two maps509* {@code m1} and {@code m2}, as required by the general contract of510* {@link Object#hashCode}.511*512* @implSpec513* This implementation iterates over {@code entrySet()}, calling514* {@link Map.Entry#hashCode hashCode()} on each element (entry) in the515* set, and adding up the results.516*517* @return the hash code value for this map518* @see Map.Entry#hashCode()519* @see Object#equals(Object)520* @see Set#equals(Object)521*/522public int hashCode() {523int h = 0;524for (Entry<K, V> entry : entrySet())525h += entry.hashCode();526return h;527}528529/**530* Returns a string representation of this map. The string representation531* consists of a list of key-value mappings in the order returned by the532* map's {@code entrySet} view's iterator, enclosed in braces533* ({@code "{}"}). Adjacent mappings are separated by the characters534* {@code ", "} (comma and space). Each key-value mapping is rendered as535* the key followed by an equals sign ({@code "="}) followed by the536* associated value. Keys and values are converted to strings as by537* {@link String#valueOf(Object)}.538*539* @return a string representation of this map540*/541public String toString() {542Iterator<Entry<K,V>> i = entrySet().iterator();543if (! i.hasNext())544return "{}";545546StringBuilder sb = new StringBuilder();547sb.append('{');548for (;;) {549Entry<K,V> e = i.next();550K key = e.getKey();551V value = e.getValue();552sb.append(key == this ? "(this Map)" : key);553sb.append('=');554sb.append(value == this ? "(this Map)" : value);555if (! i.hasNext())556return sb.append('}').toString();557sb.append(',').append(' ');558}559}560561/**562* Returns a shallow copy of this {@code AbstractMap} instance: the keys563* and values themselves are not cloned.564*565* @return a shallow copy of this map566*/567protected Object clone() throws CloneNotSupportedException {568AbstractMap<?,?> result = (AbstractMap<?,?>)super.clone();569result.keySet = null;570result.values = null;571return result;572}573574/**575* Utility method for SimpleEntry and SimpleImmutableEntry.576* Test for equality, checking for nulls.577*578* NB: Do not replace with Object.equals until JDK-8015417 is resolved.579*/580private static boolean eq(Object o1, Object o2) {581return o1 == null ? o2 == null : o1.equals(o2);582}583584// Implementation Note: SimpleEntry and SimpleImmutableEntry585// are distinct unrelated classes, even though they share586// some code. Since you can't add or subtract final-ness587// of a field in a subclass, they can't share representations,588// and the amount of duplicated code is too small to warrant589// exposing a common abstract class.590591592/**593* An Entry maintaining a key and a value. The value may be594* changed using the {@code setValue} method. Instances of595* this class are not associated with any map's entry-set view.596*597* @apiNote598* This class facilitates the process of building custom map599* implementations. For example, it may be convenient to return600* arrays of {@code SimpleEntry} instances in method601* {@code Map.entrySet().toArray}.602*603* @since 1.6604*/605public static class SimpleEntry<K,V>606implements Entry<K,V>, java.io.Serializable607{608@java.io.Serial609private static final long serialVersionUID = -8499721149061103585L;610611@SuppressWarnings("serial") // Conditionally serializable612private final K key;613@SuppressWarnings("serial") // Conditionally serializable614private V value;615616/**617* Creates an entry representing a mapping from the specified618* key to the specified value.619*620* @param key the key represented by this entry621* @param value the value represented by this entry622*/623public SimpleEntry(K key, V value) {624this.key = key;625this.value = value;626}627628/**629* Creates an entry representing the same mapping as the630* specified entry.631*632* @param entry the entry to copy633*/634public SimpleEntry(Entry<? extends K, ? extends V> entry) {635this.key = entry.getKey();636this.value = entry.getValue();637}638639/**640* Returns the key corresponding to this entry.641*642* @return the key corresponding to this entry643*/644public K getKey() {645return key;646}647648/**649* Returns the value corresponding to this entry.650*651* @return the value corresponding to this entry652*/653public V getValue() {654return value;655}656657/**658* Replaces the value corresponding to this entry with the specified659* value.660*661* @param value new value to be stored in this entry662* @return the old value corresponding to the entry663*/664public V setValue(V value) {665V oldValue = this.value;666this.value = value;667return oldValue;668}669670/**671* Compares the specified object with this entry for equality.672* Returns {@code true} if the given object is also a map entry and673* the two entries represent the same mapping. More formally, two674* entries {@code e1} and {@code e2} represent the same mapping675* if<pre>676* (e1.getKey()==null ?677* e2.getKey()==null :678* e1.getKey().equals(e2.getKey()))679* &&680* (e1.getValue()==null ?681* e2.getValue()==null :682* e1.getValue().equals(e2.getValue()))</pre>683* This ensures that the {@code equals} method works properly across684* different implementations of the {@code Map.Entry} interface.685*686* @param o object to be compared for equality with this map entry687* @return {@code true} if the specified object is equal to this map688* entry689* @see #hashCode690*/691public boolean equals(Object o) {692return o instanceof Map.Entry<?, ?> e693&& eq(key, e.getKey())694&& eq(value, e.getValue());695}696697/**698* Returns the hash code value for this map entry. The hash code699* of a map entry {@code e} is defined to be: <pre>700* (e.getKey()==null ? 0 : e.getKey().hashCode()) ^701* (e.getValue()==null ? 0 : e.getValue().hashCode())</pre>702* This ensures that {@code e1.equals(e2)} implies that703* {@code e1.hashCode()==e2.hashCode()} for any two Entries704* {@code e1} and {@code e2}, as required by the general705* contract of {@link Object#hashCode}.706*707* @return the hash code value for this map entry708* @see #equals709*/710public int hashCode() {711return (key == null ? 0 : key.hashCode()) ^712(value == null ? 0 : value.hashCode());713}714715/**716* Returns a String representation of this map entry. This717* implementation returns the string representation of this718* entry's key followed by the equals character ("{@code =}")719* followed by the string representation of this entry's value.720*721* @return a String representation of this map entry722*/723public String toString() {724return key + "=" + value;725}726727}728729/**730* An unmodifiable Entry maintaining a key and a value. This class731* does not support the {@code setValue} method. Instances of732* this class are not associated with any map's entry-set view.733*734* @apiNote735* Instances of this class are not necessarily immutable, as the key736* and value may be mutable. An instance of <i>this specific class</i>737* is unmodifiable, because the key and value references cannot be738* changed. A reference of this <i>type</i> may not be unmodifiable,739* as a subclass may be modifiable or may provide the appearance of modifiability.740* <p>741* This class may be convenient in methods that return thread-safe snapshots of742* key-value mappings. For alternatives, see the743* {@link Map#entry Map::entry} and {@link Map.Entry#copyOf Map.Entry::copyOf}744* methods.745*746* @since 1.6747*/748public static class SimpleImmutableEntry<K,V>749implements Entry<K,V>, java.io.Serializable750{751@java.io.Serial752private static final long serialVersionUID = 7138329143949025153L;753754@SuppressWarnings("serial") // Not statically typed as Serializable755private final K key;756@SuppressWarnings("serial") // Not statically typed as Serializable757private final V value;758759/**760* Creates an entry representing a mapping from the specified761* key to the specified value.762*763* @param key the key represented by this entry764* @param value the value represented by this entry765*/766public SimpleImmutableEntry(K key, V value) {767this.key = key;768this.value = value;769}770771/**772* Creates an entry representing the same mapping as the773* specified entry.774*775* @param entry the entry to copy776*/777public SimpleImmutableEntry(Entry<? extends K, ? extends V> entry) {778this.key = entry.getKey();779this.value = entry.getValue();780}781782/**783* Returns the key corresponding to this entry.784*785* @return the key corresponding to this entry786*/787public K getKey() {788return key;789}790791/**792* Returns the value corresponding to this entry.793*794* @return the value corresponding to this entry795*/796public V getValue() {797return value;798}799800/**801* Replaces the value corresponding to this entry with the specified802* value (optional operation). This implementation simply throws803* {@code UnsupportedOperationException}, as this class implements804* an unmodifiable map entry.805*806* @implSpec807* The implementation in this class always throws {@code UnsupportedOperationException}.808*809* @param value new value to be stored in this entry810* @return (Does not return)811* @throws UnsupportedOperationException always812*/813public V setValue(V value) {814throw new UnsupportedOperationException();815}816817/**818* Compares the specified object with this entry for equality.819* Returns {@code true} if the given object is also a map entry and820* the two entries represent the same mapping. More formally, two821* entries {@code e1} and {@code e2} represent the same mapping822* if<pre>823* (e1.getKey()==null ?824* e2.getKey()==null :825* e1.getKey().equals(e2.getKey()))826* &&827* (e1.getValue()==null ?828* e2.getValue()==null :829* e1.getValue().equals(e2.getValue()))</pre>830* This ensures that the {@code equals} method works properly across831* different implementations of the {@code Map.Entry} interface.832*833* @param o object to be compared for equality with this map entry834* @return {@code true} if the specified object is equal to this map835* entry836* @see #hashCode837*/838public boolean equals(Object o) {839return o instanceof Map.Entry<?, ?> e840&& eq(key, e.getKey())841&& eq(value, e.getValue());842}843844/**845* Returns the hash code value for this map entry. The hash code846* of a map entry {@code e} is defined to be: <pre>847* (e.getKey()==null ? 0 : e.getKey().hashCode()) ^848* (e.getValue()==null ? 0 : e.getValue().hashCode())</pre>849* This ensures that {@code e1.equals(e2)} implies that850* {@code e1.hashCode()==e2.hashCode()} for any two Entries851* {@code e1} and {@code e2}, as required by the general852* contract of {@link Object#hashCode}.853*854* @return the hash code value for this map entry855* @see #equals856*/857public int hashCode() {858return (key == null ? 0 : key.hashCode()) ^859(value == null ? 0 : value.hashCode());860}861862/**863* Returns a String representation of this map entry. This864* implementation returns the string representation of this865* entry's key followed by the equals character ("{@code =}")866* followed by the string representation of this entry's value.867*868* @return a String representation of this map entry869*/870public String toString() {871return key + "=" + value;872}873874}875876}877878879