001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017:
018: package java.util;
019:
020: /**
021: * AbstractMap is an abstract implementation of the Map interface. This
022: * implementation does not support adding. A subclass must implement the
023: * abstract method entrySet().
024: *
025: * @since 1.2
026: */
027: public abstract class AbstractMap<K, V> implements Map<K, V> {
028:
029: // Lazily initialized key set.
030: Set<K> keySet;
031:
032: Collection<V> valuesCollection;
033:
034: /**
035: * Constructs a new instance of this AbstractMap.
036: */
037: protected AbstractMap() {
038: super ();
039: }
040:
041: /**
042: * Removes all elements from this Map, leaving it empty.
043: *
044: * @exception UnsupportedOperationException
045: * when removing from this Map is not supported
046: *
047: * @see #isEmpty
048: * @see #size
049: */
050: public void clear() {
051: entrySet().clear();
052: }
053:
054: /**
055: * Searches this Map for the specified key.
056: *
057: * @param key
058: * the object to search for
059: * @return true if <code>key</code> is a key of this Map, false otherwise
060: */
061: public boolean containsKey(Object key) {
062: Iterator<Map.Entry<K, V>> it = entrySet().iterator();
063: if (key != null) {
064: while (it.hasNext()) {
065: if (key.equals(it.next().getKey())) {
066: return true;
067: }
068: }
069: } else {
070: while (it.hasNext()) {
071: if (it.next().getKey() == null) {
072: return true;
073: }
074: }
075: }
076: return false;
077: }
078:
079: /**
080: * Searches this Map for the specified value.
081: *
082: * @param value
083: * the object to search for
084: * @return true if <code>value</code> is a value of this Map, false
085: * otherwise
086: */
087: public boolean containsValue(Object value) {
088: Iterator<Map.Entry<K, V>> it = entrySet().iterator();
089: if (value != null) {
090: while (it.hasNext()) {
091: if (value.equals(it.next().getValue())) {
092: return true;
093: }
094: }
095: } else {
096: while (it.hasNext()) {
097: if (it.next().getValue() == null) {
098: return true;
099: }
100: }
101: }
102: return false;
103: }
104:
105: /**
106: * Returns a Set of <code>Map.Entry</code>s that represent the entries in
107: * this Map. Making changes to this Set will change the original Map and
108: * vice-versa. Entries can be removed from the Set, or their values can be
109: * changed, but new entries cannot be added to the Set.
110: *
111: * @return a Set of <code>Map.Entry</code>s representing the entries in
112: * this Map
113: */
114: public abstract Set<Map.Entry<K, V>> entrySet();
115:
116: /**
117: * Compares the specified object to this Map and answer if they are equal.
118: * The object must be an instance of Map and contain the same key/value
119: * pairs.
120: *
121: * @param object
122: * the object to compare with this object
123: * @return true if the specified object is equal to this Map, false
124: * otherwise
125: *
126: * @see #hashCode
127: */
128: @Override
129: public boolean equals(Object object) {
130: if (this == object) {
131: return true;
132: }
133: if (object instanceof Map) {
134: Map<?, ?> map = (Map<?, ?>) object;
135: if (size() != map.size()) {
136: return false;
137: }
138:
139: Iterator<Map.Entry<K, V>> it = entrySet().iterator();
140:
141: try {
142: while (it.hasNext()) {
143: Entry<K, V> entry = it.next();
144: K key = entry.getKey();
145: V value = entry.getValue();
146: Object obj = map.get(key);
147: if (null != obj && (!obj.equals(value))
148: || null == obj && obj != value) {
149: return false;
150: }
151: }
152: } catch (ClassCastException cce) {
153: return false;
154: }
155: return true;
156: }
157: return false;
158: }
159:
160: /**
161: * Answers the value of the mapping with the specified key.
162: *
163: * @param key
164: * the key
165: * @return the value of the mapping with the specified key
166: */
167: public V get(Object key) {
168: Iterator<Map.Entry<K, V>> it = entrySet().iterator();
169: if (key != null) {
170: while (it.hasNext()) {
171: Map.Entry<K, V> entry = it.next();
172: if (key.equals(entry.getKey())) {
173: return entry.getValue();
174: }
175: }
176: } else {
177: while (it.hasNext()) {
178: Map.Entry<K, V> entry = it.next();
179: if (entry.getKey() == null) {
180: return entry.getValue();
181: }
182: }
183: }
184: return null;
185: }
186:
187: /**
188: * Answers an integer hash code for the receiver. Objects which are equal
189: * answer the same value for this method.
190: *
191: * @return the receiver's hash
192: *
193: * @see #equals
194: */
195: @Override
196: public int hashCode() {
197: int result = 0;
198: Iterator<Map.Entry<K, V>> it = entrySet().iterator();
199: while (it.hasNext()) {
200: result += it.next().hashCode();
201: }
202: return result;
203: }
204:
205: /**
206: * Answers if this Map has no elements, a size of zero.
207: *
208: * @return true if this Map has no elements, false otherwise
209: *
210: * @see #size
211: */
212: public boolean isEmpty() {
213: return size() == 0;
214: }
215:
216: /**
217: * Answers a Set of the keys contained in this Map. The set is backed by
218: * this Map so changes to one are reflected by the other. The set does not
219: * support adding.
220: *
221: * @return a Set of the keys
222: */
223: public Set<K> keySet() {
224: if (keySet == null) {
225: keySet = new AbstractSet<K>() {
226: @Override
227: public boolean contains(Object object) {
228: return containsKey(object);
229: }
230:
231: @Override
232: public int size() {
233: return AbstractMap.this .size();
234: }
235:
236: @Override
237: public Iterator<K> iterator() {
238: return new Iterator<K>() {
239: Iterator<Map.Entry<K, V>> setIterator = entrySet()
240: .iterator();
241:
242: public boolean hasNext() {
243: return setIterator.hasNext();
244: }
245:
246: public K next() {
247: return setIterator.next().getKey();
248: }
249:
250: public void remove() {
251: setIterator.remove();
252: }
253: };
254: }
255: };
256: }
257: return keySet;
258: }
259:
260: /**
261: * Maps the specified key to the specified value.
262: *
263: * @param key
264: * the key
265: * @param value
266: * the value
267: * @return the value of any previous mapping with the specified key or null
268: * if there was no mapping
269: *
270: * @exception UnsupportedOperationException
271: * when adding to this Map is not supported
272: * @exception ClassCastException
273: * when the class of the key or value is inappropriate for
274: * this Map
275: * @exception IllegalArgumentException
276: * when the key or value cannot be added to this Map
277: * @exception NullPointerException
278: * when the key or value is null and this Map does not
279: * support null keys or values
280: */
281: public V put(K key, V value) {
282: throw new UnsupportedOperationException();
283: }
284:
285: /**
286: * Copies every mapping in the specified Map to this Map.
287: *
288: * @param map
289: * the Map to copy mappings from
290: *
291: * @exception UnsupportedOperationException
292: * when adding to this Map is not supported
293: * @exception ClassCastException
294: * when the class of a key or value is inappropriate for this
295: * Map
296: * @exception IllegalArgumentException
297: * when a key or value cannot be added to this Map
298: * @exception NullPointerException
299: * when a key or value is null and this Map does not support
300: * null keys or values
301: */
302: public void putAll(Map<? extends K, ? extends V> map) {
303: for (Map.Entry<? extends K, ? extends V> entry : map.entrySet()) {
304: put(entry.getKey(), entry.getValue());
305: }
306: }
307:
308: /**
309: * Removes a mapping with the specified key from this Map.
310: *
311: * @param key
312: * the key of the mapping to remove
313: * @return the value of the removed mapping or null if key is not a key in
314: * this Map
315: *
316: * @exception UnsupportedOperationException
317: * when removing from this Map is not supported
318: */
319: public V remove(Object key) {
320: Iterator<Map.Entry<K, V>> it = entrySet().iterator();
321: if (key != null) {
322: while (it.hasNext()) {
323: Map.Entry<K, V> entry = it.next();
324: if (key.equals(entry.getKey())) {
325: it.remove();
326: return entry.getValue();
327: }
328: }
329: } else {
330: while (it.hasNext()) {
331: Map.Entry<K, V> entry = it.next();
332: if (entry.getKey() == null) {
333: it.remove();
334: return entry.getValue();
335: }
336: }
337: }
338: return null;
339: }
340:
341: /**
342: * Answers the number of elements in this Map.
343: *
344: * @return the number of elements in this Map
345: */
346: public int size() {
347: return entrySet().size();
348: }
349:
350: /**
351: * Answers the string representation of this Map.
352: *
353: * @return the string representation of this Map
354: */
355: @Override
356: public String toString() {
357: if (isEmpty()) {
358: return "{}"; //$NON-NLS-1$
359: }
360:
361: StringBuilder buffer = new StringBuilder(size() * 28);
362: buffer.append('{');
363: Iterator<Map.Entry<K, V>> it = entrySet().iterator();
364: while (it.hasNext()) {
365: Map.Entry<K, V> entry = it.next();
366: Object key = entry.getKey();
367: if (key != this ) {
368: buffer.append(key);
369: } else {
370: buffer.append("(this Map)"); //$NON-NLS-1$
371: }
372: buffer.append('=');
373: Object value = entry.getValue();
374: if (value != this ) {
375: buffer.append(value);
376: } else {
377: buffer.append("(this Map)"); //$NON-NLS-1$
378: }
379: if (it.hasNext()) {
380: buffer.append(", "); //$NON-NLS-1$
381: }
382: }
383: buffer.append('}');
384: return buffer.toString();
385: }
386:
387: /**
388: * Answers a collection of the values contained in this map. The collection
389: * is backed by this map so changes to one are reflected by the other. The
390: * collection supports remove, removeAll, retainAll and clear operations,
391: * and it does not support add or addAll operations.
392: *
393: * This method answers a collection which is the subclass of
394: * AbstractCollection. The iterator method of this subclass answers a
395: * "wrapper object" over the iterator of map's entrySet(). The size method
396: * wraps the map's size method and the contains method wraps the map's
397: * containsValue method.
398: *
399: * The collection is created when this method is called at first time and
400: * returned in response to all subsequent calls. This method may return
401: * different Collection when multiple calls to this method, since it has no
402: * synchronization performed.
403: *
404: * @return a collection of the values contained in this map
405: *
406: */
407: public Collection<V> values() {
408: if (valuesCollection == null) {
409: valuesCollection = new AbstractCollection<V>() {
410: @Override
411: public int size() {
412: return AbstractMap.this .size();
413: }
414:
415: @Override
416: public boolean contains(Object object) {
417: return containsValue(object);
418: }
419:
420: @Override
421: public Iterator<V> iterator() {
422: return new Iterator<V>() {
423: Iterator<Map.Entry<K, V>> setIterator = entrySet()
424: .iterator();
425:
426: public boolean hasNext() {
427: return setIterator.hasNext();
428: }
429:
430: public V next() {
431: return setIterator.next().getValue();
432: }
433:
434: public void remove() {
435: setIterator.remove();
436: }
437: };
438: }
439: };
440: }
441: return valuesCollection;
442: }
443:
444: /**
445: * Answers a new instance of the same class as the receiver, whose slots
446: * have been filled in with the values in the slots of the receiver.
447: *
448: * @return Object a shallow copy of this object.
449: * @exception CloneNotSupportedException
450: * if the receiver's class does not implement the interface
451: * Cloneable.
452: */
453: @Override
454: @SuppressWarnings("unchecked")
455: protected Object clone() throws CloneNotSupportedException {
456: AbstractMap<K, V> result = (AbstractMap<K, V>) super.clone();
457: result.keySet = null;
458: result.valuesCollection = null;
459: return result;
460: }
461: }
|