Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/src/share/classes/java/util/AbstractMap.java
38829 views
/*1* Copyright (c) 1997, 2013, 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 <tt>Map</tt>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 <tt>entrySet</tt> method, which34* returns a set-view of the map's mappings. Typically, the returned set35* will, in turn, be implemented atop <tt>AbstractSet</tt>. This set should36* not support the <tt>add</tt> or <tt>remove</tt> methods, and its iterator37* should not support the <tt>remove</tt> method.38*39* <p>To implement a modifiable map, the programmer must additionally override40* this class's <tt>put</tt> method (which otherwise throws an41* <tt>UnsupportedOperationException</tt>), and the iterator returned by42* <tt>entrySet().iterator()</tt> must additionally implement its43* <tt>remove</tt> method.44*45* <p>The programmer should generally provide a void (no argument) and map46* constructor, as per the recommendation in the <tt>Map</tt> 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}/../technotes/guides/collections/index.html">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 <tt>entrySet().size()</tt>.82*/83public int size() {84return entrySet().size();85}8687/**88* {@inheritDoc}89*90* @implSpec91* This implementation returns <tt>size() == 0</tt>.92*/93public boolean isEmpty() {94return size() == 0;95}9697/**98* {@inheritDoc}99*100* @implSpec101* This implementation iterates over <tt>entrySet()</tt> searching102* for an entry with the specified value. If such an entry is found,103* <tt>true</tt> is returned. If the iteration terminates without104* finding such an entry, <tt>false</tt> 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 <tt>entrySet()</tt> searching133* for an entry with the specified key. If such an entry is found,134* <tt>true</tt> is returned. If the iteration terminates without135* finding such an entry, <tt>false</tt> 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 <tt>entrySet()</tt> 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, <tt>null</tt> 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* <tt>UnsupportedOperationException</tt>.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 <tt>entrySet()</tt> searching for an216* entry with the specified key. If such an entry is found, its value is217* obtained with its <tt>getValue</tt> operation, the entry is removed218* from the collection (and the backing map) with the iterator's219* <tt>remove</tt> operation, and the saved value is returned. If the220* iteration terminates without finding such an entry, <tt>null</tt> 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* <tt>UnsupportedOperationException</tt> if the <tt>entrySet</tt>226* iterator does not support the <tt>remove</tt> 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* <tt>entrySet()</tt> collection, and calls this map's <tt>put</tt>267* operation once for each entry returned by the iteration.268*269* <p>Note that this implementation throws an270* <tt>UnsupportedOperationException</tt> if this map does not support271* the <tt>put</tt> 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 <tt>entrySet().clear()</tt>.288*289* <p>Note that this implementation throws an290* <tt>UnsupportedOperationException</tt> if the <tt>entrySet</tt>291* does not support the <tt>clear</tt> 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 <tt>entrySet()</tt> iterator. The <tt>size</tt> method336* delegates to this map's <tt>size</tt> method and the337* <tt>contains</tt> method delegates to this map's338* <tt>containsKey</tt> 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 <tt>entrySet()</tt> iterator.395* The <tt>size</tt> method delegates to this map's <tt>size</tt>396* method and the <tt>contains</tt> method delegates to this map's397* <tt>containsValue</tt> 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* <tt>true</tt> if the given object is also a map and the two maps455* represent the same mappings. More formally, two maps <tt>m1</tt> and456* <tt>m2</tt> represent the same mappings if457* <tt>m1.entrySet().equals(m2.entrySet())</tt>. This ensures that the458* <tt>equals</tt> method works properly across different implementations459* of the <tt>Map</tt> interface.460*461* @implSpec462* This implementation first checks if the specified object is this map;463* if so it returns <tt>true</tt>. Then, it checks if the specified464* object is a map whose size is identical to the size of this map; if465* not, it returns <tt>false</tt>. If so, it iterates over this map's466* <tt>entrySet</tt> collection, and checks that the specified map467* contains each mapping that this map contains. If the specified map468* fails to contain such a mapping, <tt>false</tt> is returned. If the469* iteration completes, <tt>true</tt> is returned.470*471* @param o object to be compared for equality with this map472* @return <tt>true</tt> if the specified object is equal to this map473*/474public boolean equals(Object o) {475if (o == this)476return true;477478if (!(o instanceof Map))479return false;480Map<?,?> m = (Map<?,?>) o;481if (m.size() != size())482return false;483484try {485Iterator<Entry<K,V>> i = entrySet().iterator();486while (i.hasNext()) {487Entry<K,V> e = i.next();488K key = e.getKey();489V value = e.getValue();490if (value == null) {491if (!(m.get(key)==null && m.containsKey(key)))492return false;493} else {494if (!value.equals(m.get(key)))495return false;496}497}498} catch (ClassCastException unused) {499return false;500} catch (NullPointerException unused) {501return false;502}503504return true;505}506507/**508* Returns the hash code value for this map. The hash code of a map is509* defined to be the sum of the hash codes of each entry in the map's510* <tt>entrySet()</tt> view. This ensures that <tt>m1.equals(m2)</tt>511* implies that <tt>m1.hashCode()==m2.hashCode()</tt> for any two maps512* <tt>m1</tt> and <tt>m2</tt>, as required by the general contract of513* {@link Object#hashCode}.514*515* @implSpec516* This implementation iterates over <tt>entrySet()</tt>, calling517* {@link Map.Entry#hashCode hashCode()} on each element (entry) in the518* set, and adding up the results.519*520* @return the hash code value for this map521* @see Map.Entry#hashCode()522* @see Object#equals(Object)523* @see Set#equals(Object)524*/525public int hashCode() {526int h = 0;527Iterator<Entry<K,V>> i = entrySet().iterator();528while (i.hasNext())529h += i.next().hashCode();530return h;531}532533/**534* Returns a string representation of this map. The string representation535* consists of a list of key-value mappings in the order returned by the536* map's <tt>entrySet</tt> view's iterator, enclosed in braces537* (<tt>"{}"</tt>). Adjacent mappings are separated by the characters538* <tt>", "</tt> (comma and space). Each key-value mapping is rendered as539* the key followed by an equals sign (<tt>"="</tt>) followed by the540* associated value. Keys and values are converted to strings as by541* {@link String#valueOf(Object)}.542*543* @return a string representation of this map544*/545public String toString() {546Iterator<Entry<K,V>> i = entrySet().iterator();547if (! i.hasNext())548return "{}";549550StringBuilder sb = new StringBuilder();551sb.append('{');552for (;;) {553Entry<K,V> e = i.next();554K key = e.getKey();555V value = e.getValue();556sb.append(key == this ? "(this Map)" : key);557sb.append('=');558sb.append(value == this ? "(this Map)" : value);559if (! i.hasNext())560return sb.append('}').toString();561sb.append(',').append(' ');562}563}564565/**566* Returns a shallow copy of this <tt>AbstractMap</tt> instance: the keys567* and values themselves are not cloned.568*569* @return a shallow copy of this map570*/571protected Object clone() throws CloneNotSupportedException {572AbstractMap<?,?> result = (AbstractMap<?,?>)super.clone();573result.keySet = null;574result.values = null;575return result;576}577578/**579* Utility method for SimpleEntry and SimpleImmutableEntry.580* Test for equality, checking for nulls.581*582* NB: Do not replace with Object.equals until JDK-8015417 is resolved.583*/584private static boolean eq(Object o1, Object o2) {585return o1 == null ? o2 == null : o1.equals(o2);586}587588// Implementation Note: SimpleEntry and SimpleImmutableEntry589// are distinct unrelated classes, even though they share590// some code. Since you can't add or subtract final-ness591// of a field in a subclass, they can't share representations,592// and the amount of duplicated code is too small to warrant593// exposing a common abstract class.594595596/**597* An Entry maintaining a key and a value. The value may be598* changed using the <tt>setValue</tt> method. This class599* facilitates the process of building custom map600* implementations. For example, it may be convenient to return601* arrays of <tt>SimpleEntry</tt> instances in method602* <tt>Map.entrySet().toArray</tt>.603*604* @since 1.6605*/606public static class SimpleEntry<K,V>607implements Entry<K,V>, java.io.Serializable608{609private static final long serialVersionUID = -8499721149061103585L;610611private final K key;612private V value;613614/**615* Creates an entry representing a mapping from the specified616* key to the specified value.617*618* @param key the key represented by this entry619* @param value the value represented by this entry620*/621public SimpleEntry(K key, V value) {622this.key = key;623this.value = value;624}625626/**627* Creates an entry representing the same mapping as the628* specified entry.629*630* @param entry the entry to copy631*/632public SimpleEntry(Entry<? extends K, ? extends V> entry) {633this.key = entry.getKey();634this.value = entry.getValue();635}636637/**638* Returns the key corresponding to this entry.639*640* @return the key corresponding to this entry641*/642public K getKey() {643return key;644}645646/**647* Returns the value corresponding to this entry.648*649* @return the value corresponding to this entry650*/651public V getValue() {652return value;653}654655/**656* Replaces the value corresponding to this entry with the specified657* value.658*659* @param value new value to be stored in this entry660* @return the old value corresponding to the entry661*/662public V setValue(V value) {663V oldValue = this.value;664this.value = value;665return oldValue;666}667668/**669* Compares the specified object with this entry for equality.670* Returns {@code true} if the given object is also a map entry and671* the two entries represent the same mapping. More formally, two672* entries {@code e1} and {@code e2} represent the same mapping673* if<pre>674* (e1.getKey()==null ?675* e2.getKey()==null :676* e1.getKey().equals(e2.getKey()))677* &&678* (e1.getValue()==null ?679* e2.getValue()==null :680* e1.getValue().equals(e2.getValue()))</pre>681* This ensures that the {@code equals} method works properly across682* different implementations of the {@code Map.Entry} interface.683*684* @param o object to be compared for equality with this map entry685* @return {@code true} if the specified object is equal to this map686* entry687* @see #hashCode688*/689public boolean equals(Object o) {690if (!(o instanceof Map.Entry))691return false;692Map.Entry<?,?> e = (Map.Entry<?,?>)o;693return eq(key, e.getKey()) && eq(value, e.getValue());694}695696/**697* Returns the hash code value for this map entry. The hash code698* of a map entry {@code e} is defined to be: <pre>699* (e.getKey()==null ? 0 : e.getKey().hashCode()) ^700* (e.getValue()==null ? 0 : e.getValue().hashCode())</pre>701* This ensures that {@code e1.equals(e2)} implies that702* {@code e1.hashCode()==e2.hashCode()} for any two Entries703* {@code e1} and {@code e2}, as required by the general704* contract of {@link Object#hashCode}.705*706* @return the hash code value for this map entry707* @see #equals708*/709public int hashCode() {710return (key == null ? 0 : key.hashCode()) ^711(value == null ? 0 : value.hashCode());712}713714/**715* Returns a String representation of this map entry. This716* implementation returns the string representation of this717* entry's key followed by the equals character ("<tt>=</tt>")718* followed by the string representation of this entry's value.719*720* @return a String representation of this map entry721*/722public String toString() {723return key + "=" + value;724}725726}727728/**729* An Entry maintaining an immutable key and value. This class730* does not support method <tt>setValue</tt>. This class may be731* convenient in methods that return thread-safe snapshots of732* key-value mappings.733*734* @since 1.6735*/736public static class SimpleImmutableEntry<K,V>737implements Entry<K,V>, java.io.Serializable738{739private static final long serialVersionUID = 7138329143949025153L;740741private final K key;742private final V value;743744/**745* Creates an entry representing a mapping from the specified746* key to the specified value.747*748* @param key the key represented by this entry749* @param value the value represented by this entry750*/751public SimpleImmutableEntry(K key, V value) {752this.key = key;753this.value = value;754}755756/**757* Creates an entry representing the same mapping as the758* specified entry.759*760* @param entry the entry to copy761*/762public SimpleImmutableEntry(Entry<? extends K, ? extends V> entry) {763this.key = entry.getKey();764this.value = entry.getValue();765}766767/**768* Returns the key corresponding to this entry.769*770* @return the key corresponding to this entry771*/772public K getKey() {773return key;774}775776/**777* Returns the value corresponding to this entry.778*779* @return the value corresponding to this entry780*/781public V getValue() {782return value;783}784785/**786* Replaces the value corresponding to this entry with the specified787* value (optional operation). This implementation simply throws788* <tt>UnsupportedOperationException</tt>, as this class implements789* an <i>immutable</i> map entry.790*791* @param value new value to be stored in this entry792* @return (Does not return)793* @throws UnsupportedOperationException always794*/795public V setValue(V value) {796throw new UnsupportedOperationException();797}798799/**800* Compares the specified object with this entry for equality.801* Returns {@code true} if the given object is also a map entry and802* the two entries represent the same mapping. More formally, two803* entries {@code e1} and {@code e2} represent the same mapping804* if<pre>805* (e1.getKey()==null ?806* e2.getKey()==null :807* e1.getKey().equals(e2.getKey()))808* &&809* (e1.getValue()==null ?810* e2.getValue()==null :811* e1.getValue().equals(e2.getValue()))</pre>812* This ensures that the {@code equals} method works properly across813* different implementations of the {@code Map.Entry} interface.814*815* @param o object to be compared for equality with this map entry816* @return {@code true} if the specified object is equal to this map817* entry818* @see #hashCode819*/820public boolean equals(Object o) {821if (!(o instanceof Map.Entry))822return false;823Map.Entry<?,?> e = (Map.Entry<?,?>)o;824return eq(key, e.getKey()) && eq(value, e.getValue());825}826827/**828* Returns the hash code value for this map entry. The hash code829* of a map entry {@code e} is defined to be: <pre>830* (e.getKey()==null ? 0 : e.getKey().hashCode()) ^831* (e.getValue()==null ? 0 : e.getValue().hashCode())</pre>832* This ensures that {@code e1.equals(e2)} implies that833* {@code e1.hashCode()==e2.hashCode()} for any two Entries834* {@code e1} and {@code e2}, as required by the general835* contract of {@link Object#hashCode}.836*837* @return the hash code value for this map entry838* @see #equals839*/840public int hashCode() {841return (key == null ? 0 : key.hashCode()) ^842(value == null ? 0 : value.hashCode());843}844845/**846* Returns a String representation of this map entry. This847* implementation returns the string representation of this848* entry's key followed by the equals character ("<tt>=</tt>")849* followed by the string representation of this entry's value.850*851* @return a String representation of this map entry852*/853public String toString() {854return key + "=" + value;855}856857}858859}860861862