1. /*
  2. * @(#)HashMap.java 1.38 00/02/02
  3. *
  4. * Copyright 1997-2000 Sun Microsystems, Inc. All Rights Reserved.
  5. *
  6. * This software is the proprietary information of Sun Microsystems, Inc.
  7. * Use is subject to license terms.
  8. *
  9. */
  10. package java.util;
  11. import java.io.*;
  12. /**
  13. * Hash table based implementation of the <tt>Map</tt> interface. This
  14. * implementation provides all of the optional map operations, and permits
  15. * <tt>null</tt> values and the <tt>null</tt> key. (The <tt>HashMap</tt>
  16. * class is roughly equivalent to <tt>Hashtable</tt>, except that it is
  17. * unsynchronized and permits nulls.) This class makes no guarantees as to
  18. * the order of the map; in particular, it does not guarantee that the order
  19. * will remain constant over time.<p>
  20. *
  21. * This implementation provides constant-time performance for the basic
  22. * operations (<tt>get</tt> and <tt>put</tt>), assuming the hash function
  23. * disperses the elements properly among the buckets. Iteration over
  24. * collection views requires time proportional to the "capacity" of the
  25. * <tt>HashMap</tt> instance (the number of buckets) plus its size (the number
  26. * of key-value mappings). Thus, it's very important not to set the intial
  27. * capacity too high (or the load factor too low) if iteration performance is
  28. * important.<p>
  29. *
  30. * An instance of <tt>HashMap</tt> has two parameters that affect its
  31. * performance: <i>initial capacity</i> and <i>load factor</i>. The
  32. * <i>capacity</i> is the number of buckets in the hash table, and the initial
  33. * capacity is simply the capacity at the time the hash table is created. The
  34. * <i>load factor</i> is a measure of how full the hash table is allowed to
  35. * get before its capacity is automatically increased. When the number of
  36. * entries in the hash table exceeds the product of the load factor and the
  37. * current capacity, the capacity is roughly doubled by calling the
  38. * <tt>rehash</tt> method.<p>
  39. *
  40. * As a general rule, the default load factor (.75) offers a good tradeoff
  41. * between time and space costs. Higher values decrease the space overhead
  42. * but increase the lookup cost (reflected in most of the operations of the
  43. * <tt>HashMap</tt> class, including <tt>get</tt> and <tt>put</tt>). The
  44. * expected number of entries in the map and its load factor should be taken
  45. * into account when setting its initial capacity, so as to minimize the
  46. * number of <tt>rehash</tt> operations. If the initial capacity is greater
  47. * than the maximum number of entries divided by the load factor, no
  48. * <tt>rehash</tt> operations will ever occur.<p>
  49. *
  50. * If many mappings are to be stored in a <tt>HashMap</tt> instance, creating
  51. * it with a sufficiently large capacity will allow the mappings to be stored
  52. * more efficiently than letting it perform automatic rehashing as needed to
  53. * grow the table.<p>
  54. *
  55. * <b>Note that this implementation is not synchronized.</b> If multiple
  56. * threads access this map concurrently, and at least one of the threads
  57. * modifies the map structurally, it <i>must</i> be synchronized externally.
  58. * (A structural modification is any operation that adds or deletes one or
  59. * more mappings; merely changing the value associated with a key that an
  60. * instance already contains is not a structural modification.) This is
  61. * typically accomplished by synchronizing on some object that naturally
  62. * encapsulates the map. If no such object exists, the map should be
  63. * "wrapped" using the <tt>Collections.synchronizedMap</tt> method. This is
  64. * best done at creation time, to prevent accidental unsynchronized access to
  65. * the map: <pre> Map m = Collections.synchronizedMap(new HashMap(...));
  66. * </pre><p>
  67. *
  68. * The iterators returned by all of this class's "collection view methods" are
  69. * <i>fail-fast</i>: if the map is structurally modified at any time after the
  70. * iterator is created, in any way except through the iterator's own
  71. * <tt>remove</tt> or <tt>add</tt> methods, the iterator will throw a
  72. * <tt>ConcurrentModificationException</tt>. Thus, in the face of concurrent
  73. * modification, the iterator fails quickly and cleanly, rather than risking
  74. * arbitrary, non-deterministic behavior at an undetermined time in the
  75. * future.
  76. *
  77. * @author Josh Bloch
  78. * @author Arthur van Hoff
  79. * @version 1.38, 02/02/00
  80. * @see Object#hashCode()
  81. * @see Collection
  82. * @see Map
  83. * @see TreeMap
  84. * @see Hashtable
  85. * @since 1.2
  86. */
  87. public class HashMap extends AbstractMap implements Map, Cloneable,
  88. java.io.Serializable {
  89. /**
  90. * The hash table data.
  91. */
  92. private transient Entry table[];
  93. /**
  94. * The total number of mappings in the hash table.
  95. */
  96. private transient int count;
  97. /**
  98. * The table is rehashed when its size exceeds this threshold. (The
  99. * value of this field is (int)(capacity * loadFactor).)
  100. *
  101. * @serial
  102. */
  103. private int threshold;
  104. /**
  105. * The load factor for the hashtable.
  106. *
  107. * @serial
  108. */
  109. private float loadFactor;
  110. /**
  111. * The number of times this HashMap has been structurally modified
  112. * Structural modifications are those that change the number of mappings in
  113. * the HashMap or otherwise modify its internal structure (e.g.,
  114. * rehash). This field is used to make iterators on Collection-views of
  115. * the HashMap fail-fast. (See ConcurrentModificationException).
  116. */
  117. private transient int modCount = 0;
  118. /**
  119. * Constructs a new, empty map with the specified initial
  120. * capacity and the specified load factor.
  121. *
  122. * @param initialCapacity the initial capacity of the HashMap.
  123. * @param loadFactor the load factor of the HashMap
  124. * @throws IllegalArgumentException if the initial capacity is less
  125. * than zero, or if the load factor is nonpositive.
  126. */
  127. public HashMap(int initialCapacity, float loadFactor) {
  128. if (initialCapacity < 0)
  129. throw new IllegalArgumentException("Illegal Initial Capacity: "+
  130. initialCapacity);
  131. if (loadFactor <= 0 || Float.isNaN(loadFactor))
  132. throw new IllegalArgumentException("Illegal Load factor: "+
  133. loadFactor);
  134. if (initialCapacity==0)
  135. initialCapacity = 1;
  136. this.loadFactor = loadFactor;
  137. table = new Entry[initialCapacity];
  138. threshold = (int)(initialCapacity * loadFactor);
  139. }
  140. /**
  141. * Constructs a new, empty map with the specified initial capacity
  142. * and default load factor, which is <tt>0.75</tt>.
  143. *
  144. * @param initialCapacity the initial capacity of the HashMap.
  145. * @throws IllegalArgumentException if the initial capacity is less
  146. * than zero.
  147. */
  148. public HashMap(int initialCapacity) {
  149. this(initialCapacity, 0.75f);
  150. }
  151. /**
  152. * Constructs a new, empty map with a default capacity and load
  153. * factor, which is <tt>0.75</tt>.
  154. */
  155. public HashMap() {
  156. this(11, 0.75f);
  157. }
  158. /**
  159. * Constructs a new map with the same mappings as the given map. The
  160. * map is created with a capacity of twice the number of mappings in
  161. * the given map or 11 (whichever is greater), and a default load factor,
  162. * which is <tt>0.75</tt>.
  163. *
  164. * @param t the map whose mappings are to be placed in this map.
  165. */
  166. public HashMap(Map t) {
  167. this(Math.max(2*t.size(), 11), 0.75f);
  168. putAll(t);
  169. }
  170. /**
  171. * Returns the number of key-value mappings in this map.
  172. *
  173. * @return the number of key-value mappings in this map.
  174. */
  175. public int size() {
  176. return count;
  177. }
  178. /**
  179. * Returns <tt>true</tt> if this map contains no key-value mappings.
  180. *
  181. * @return <tt>true</tt> if this map contains no key-value mappings.
  182. */
  183. public boolean isEmpty() {
  184. return count == 0;
  185. }
  186. /**
  187. * Returns <tt>true</tt> if this map maps one or more keys to the
  188. * specified value.
  189. *
  190. * @param value value whose presence in this map is to be tested.
  191. * @return <tt>true</tt> if this map maps one or more keys to the
  192. * specified value.
  193. */
  194. public boolean containsValue(Object value) {
  195. Entry tab[] = table;
  196. if (value==null) {
  197. for (int i = tab.length ; i-- > 0 ;)
  198. for (Entry e = tab[i] ; e != null ; e = e.next)
  199. if (e.value==null)
  200. return true;
  201. } else {
  202. for (int i = tab.length ; i-- > 0 ;)
  203. for (Entry e = tab[i] ; e != null ; e = e.next)
  204. if (value.equals(e.value))
  205. return true;
  206. }
  207. return false;
  208. }
  209. /**
  210. * Returns <tt>true</tt> if this map contains a mapping for the specified
  211. * key.
  212. *
  213. * @return <tt>true</tt> if this map contains a mapping for the specified
  214. * key.
  215. * @param key key whose presence in this Map is to be tested.
  216. */
  217. public boolean containsKey(Object key) {
  218. Entry tab[] = table;
  219. if (key != null) {
  220. int hash = key.hashCode();
  221. int index = (hash & 0x7FFFFFFF) % tab.length;
  222. for (Entry e = tab[index]; e != null; e = e.next)
  223. if (e.hash==hash && key.equals(e.key))
  224. return true;
  225. } else {
  226. for (Entry e = tab[0]; e != null; e = e.next)
  227. if (e.key==null)
  228. return true;
  229. }
  230. return false;
  231. }
  232. /**
  233. * Returns the value to which this map maps the specified key. Returns
  234. * <tt>null</tt> if the map contains no mapping for this key. A return
  235. * value of <tt>null</tt> does not <i>necessarily</i> indicate that the
  236. * map contains no mapping for the key; it's also possible that the map
  237. * explicitly maps the key to <tt>null</tt>. The <tt>containsKey</tt>
  238. * operation may be used to distinguish these two cases.
  239. *
  240. * @return the value to which this map maps the specified key.
  241. * @param key key whose associated value is to be returned.
  242. */
  243. public Object get(Object key) {
  244. Entry tab[] = table;
  245. if (key != null) {
  246. int hash = key.hashCode();
  247. int index = (hash & 0x7FFFFFFF) % tab.length;
  248. for (Entry e = tab[index]; e != null; e = e.next)
  249. if ((e.hash == hash) && key.equals(e.key))
  250. return e.value;
  251. } else {
  252. for (Entry e = tab[0]; e != null; e = e.next)
  253. if (e.key==null)
  254. return e.value;
  255. }
  256. return null;
  257. }
  258. /**
  259. * Rehashes the contents of this map into a new <tt>HashMap</tt> instance
  260. * with a larger capacity. This method is called automatically when the
  261. * number of keys in this map exceeds its capacity and load factor.
  262. */
  263. private void rehash() {
  264. int oldCapacity = table.length;
  265. Entry oldMap[] = table;
  266. int newCapacity = oldCapacity * 2 + 1;
  267. Entry newMap[] = new Entry[newCapacity];
  268. modCount++;
  269. threshold = (int)(newCapacity * loadFactor);
  270. table = newMap;
  271. for (int i = oldCapacity ; i-- > 0 ;) {
  272. for (Entry old = oldMap[i] ; old != null ; ) {
  273. Entry e = old;
  274. old = old.next;
  275. int index = (e.hash & 0x7FFFFFFF) % newCapacity;
  276. e.next = newMap[index];
  277. newMap[index] = e;
  278. }
  279. }
  280. }
  281. /**
  282. * Associates the specified value with the specified key in this map.
  283. * If the map previously contained a mapping for this key, the old
  284. * value is replaced.
  285. *
  286. * @param key key with which the specified value is to be associated.
  287. * @param value value to be associated with the specified key.
  288. * @return previous value associated with specified key, or <tt>null</tt>
  289. * if there was no mapping for key. A <tt>null</tt> return can
  290. * also indicate that the HashMap previously associated
  291. * <tt>null</tt> with the specified key.
  292. */
  293. public Object put(Object key, Object value) {
  294. // Makes sure the key is not already in the HashMap.
  295. Entry tab[] = table;
  296. int hash = 0;
  297. int index = 0;
  298. if (key != null) {
  299. hash = key.hashCode();
  300. index = (hash & 0x7FFFFFFF) % tab.length;
  301. for (Entry e = tab[index] ; e != null ; e = e.next) {
  302. if ((e.hash == hash) && key.equals(e.key)) {
  303. Object old = e.value;
  304. e.value = value;
  305. return old;
  306. }
  307. }
  308. } else {
  309. for (Entry e = tab[0] ; e != null ; e = e.next) {
  310. if (e.key == null) {
  311. Object old = e.value;
  312. e.value = value;
  313. return old;
  314. }
  315. }
  316. }
  317. modCount++;
  318. if (count >= threshold) {
  319. // Rehash the table if the threshold is exceeded
  320. rehash();
  321. tab = table;
  322. index = (hash & 0x7FFFFFFF) % tab.length;
  323. }
  324. // Creates the new entry.
  325. Entry e = new Entry(hash, key, value, tab[index]);
  326. tab[index] = e;
  327. count++;
  328. return null;
  329. }
  330. /**
  331. * Removes the mapping for this key from this map if present.
  332. *
  333. * @param key key whose mapping is to be removed from the map.
  334. * @return previous value associated with specified key, or <tt>null</tt>
  335. * if there was no mapping for key. A <tt>null</tt> return can
  336. * also indicate that the map previously associated <tt>null</tt>
  337. * with the specified key.
  338. */
  339. public Object remove(Object key) {
  340. Entry tab[] = table;
  341. if (key != null) {
  342. int hash = key.hashCode();
  343. int index = (hash & 0x7FFFFFFF) % tab.length;
  344. for (Entry e = tab[index], prev = null; e != null;
  345. prev = e, e = e.next) {
  346. if ((e.hash == hash) && key.equals(e.key)) {
  347. modCount++;
  348. if (prev != null)
  349. prev.next = e.next;
  350. else
  351. tab[index] = e.next;
  352. count--;
  353. Object oldValue = e.value;
  354. e.value = null;
  355. return oldValue;
  356. }
  357. }
  358. } else {
  359. for (Entry e = tab[0], prev = null; e != null;
  360. prev = e, e = e.next) {
  361. if (e.key == null) {
  362. modCount++;
  363. if (prev != null)
  364. prev.next = e.next;
  365. else
  366. tab[0] = e.next;
  367. count--;
  368. Object oldValue = e.value;
  369. e.value = null;
  370. return oldValue;
  371. }
  372. }
  373. }
  374. return null;
  375. }
  376. /**
  377. * Copies all of the mappings from the specified map to this one.
  378. *
  379. * These mappings replace any mappings that this map had for any of the
  380. * keys currently in the specified Map.
  381. *
  382. * @param t Mappings to be stored in this map.
  383. */
  384. public void putAll(Map t) {
  385. Iterator i = t.entrySet().iterator();
  386. while (i.hasNext()) {
  387. Map.Entry e = (Map.Entry) i.next();
  388. put(e.getKey(), e.getValue());
  389. }
  390. }
  391. /**
  392. * Removes all mappings from this map.
  393. */
  394. public void clear() {
  395. Entry tab[] = table;
  396. modCount++;
  397. for (int index = tab.length; --index >= 0; )
  398. tab[index] = null;
  399. count = 0;
  400. }
  401. /**
  402. * Returns a shallow copy of this <tt>HashMap</tt> instance: the keys and
  403. * values themselves are not cloned.
  404. *
  405. * @return a shallow copy of this map.
  406. */
  407. public Object clone() {
  408. try {
  409. HashMap t = (HashMap)super.clone();
  410. t.table = new Entry[table.length];
  411. for (int i = table.length ; i-- > 0 ; ) {
  412. t.table[i] = (table[i] != null)
  413. ? (Entry)table[i].clone() : null;
  414. }
  415. t.keySet = null;
  416. t.entrySet = null;
  417. t.values = null;
  418. t.modCount = 0;
  419. return t;
  420. } catch (CloneNotSupportedException e) {
  421. // this shouldn't happen, since we are Cloneable
  422. throw new InternalError();
  423. }
  424. }
  425. // Views
  426. private transient Set keySet = null;
  427. private transient Set entrySet = null;
  428. private transient Collection values = null;
  429. /**
  430. * Returns a set view of the keys contained in this map. The set is
  431. * backed by the map, so changes to the map are reflected in the set, and
  432. * vice-versa. The set supports element removal, which removes the
  433. * corresponding mapping from this map, via the <tt>Iterator.remove</tt>,
  434. * <tt>Set.remove</tt>, <tt>removeAll</tt>, <tt>retainAll</tt>, and
  435. * <tt>clear</tt> operations. It does not support the <tt>add</tt> or
  436. * <tt>addAll</tt> operations.
  437. *
  438. * @return a set view of the keys contained in this map.
  439. */
  440. public Set keySet() {
  441. if (keySet == null) {
  442. keySet = new AbstractSet() {
  443. public Iterator iterator() {
  444. return getHashIterator(KEYS);
  445. }
  446. public int size() {
  447. return count;
  448. }
  449. public boolean contains(Object o) {
  450. return containsKey(o);
  451. }
  452. public boolean remove(Object o) {
  453. int oldSize = count;
  454. HashMap.this.remove(o);
  455. return count != oldSize;
  456. }
  457. public void clear() {
  458. HashMap.this.clear();
  459. }
  460. };
  461. }
  462. return keySet;
  463. }
  464. /**
  465. * Returns a collection view of the values contained in this map. The
  466. * collection is backed by the map, so changes to the map are reflected in
  467. * the collection, and vice-versa. The collection supports element
  468. * removal, which removes the corresponding mapping from this map, via the
  469. * <tt>Iterator.remove</tt>, <tt>Collection.remove</tt>,
  470. * <tt>removeAll</tt>, <tt>retainAll</tt>, and <tt>clear</tt> operations.
  471. * It does not support the <tt>add</tt> or <tt>addAll</tt> operations.
  472. *
  473. * @return a collection view of the values contained in this map.
  474. */
  475. public Collection values() {
  476. if (values==null) {
  477. values = new AbstractCollection() {
  478. public Iterator iterator() {
  479. return getHashIterator(VALUES);
  480. }
  481. public int size() {
  482. return count;
  483. }
  484. public boolean contains(Object o) {
  485. return containsValue(o);
  486. }
  487. public void clear() {
  488. HashMap.this.clear();
  489. }
  490. };
  491. }
  492. return values;
  493. }
  494. /**
  495. * Returns a collection view of the mappings contained in this map. Each
  496. * element in the returned collection is a <tt>Map.Entry</tt>. The
  497. * collection is backed by the map, so changes to the map are reflected in
  498. * the collection, and vice-versa. The collection supports element
  499. * removal, which removes the corresponding mapping from the map, via the
  500. * <tt>Iterator.remove</tt>, <tt>Collection.remove</tt>,
  501. * <tt>removeAll</tt>, <tt>retainAll</tt>, and <tt>clear</tt> operations.
  502. * It does not support the <tt>add</tt> or <tt>addAll</tt> operations.
  503. *
  504. * @return a collection view of the mappings contained in this map.
  505. * @see Map.Entry
  506. */
  507. public Set entrySet() {
  508. if (entrySet==null) {
  509. entrySet = new AbstractSet() {
  510. public Iterator iterator() {
  511. return getHashIterator(ENTRIES);
  512. }
  513. public boolean contains(Object o) {
  514. if (!(o instanceof Map.Entry))
  515. return false;
  516. Map.Entry entry = (Map.Entry)o;
  517. Object key = entry.getKey();
  518. Entry tab[] = table;
  519. int hash = (key==null ? 0 : key.hashCode());
  520. int index = (hash & 0x7FFFFFFF) % tab.length;
  521. for (Entry e = tab[index]; e != null; e = e.next)
  522. if (e.hash==hash && e.equals(entry))
  523. return true;
  524. return false;
  525. }
  526. public boolean remove(Object o) {
  527. if (!(o instanceof Map.Entry))
  528. return false;
  529. Map.Entry entry = (Map.Entry)o;
  530. Object key = entry.getKey();
  531. Entry tab[] = table;
  532. int hash = (key==null ? 0 : key.hashCode());
  533. int index = (hash & 0x7FFFFFFF) % tab.length;
  534. for (Entry e = tab[index], prev = null; e != null;
  535. prev = e, e = e.next) {
  536. if (e.hash==hash && e.equals(entry)) {
  537. modCount++;
  538. if (prev != null)
  539. prev.next = e.next;
  540. else
  541. tab[index] = e.next;
  542. count--;
  543. e.value = null;
  544. return true;
  545. }
  546. }
  547. return false;
  548. }
  549. public int size() {
  550. return count;
  551. }
  552. public void clear() {
  553. HashMap.this.clear();
  554. }
  555. };
  556. }
  557. return entrySet;
  558. }
  559. private Iterator getHashIterator(int type) {
  560. if (count == 0) {
  561. return emptyHashIterator;
  562. } else {
  563. return new HashIterator(type);
  564. }
  565. }
  566. /**
  567. * HashMap collision list entry.
  568. */
  569. private static class Entry implements Map.Entry {
  570. int hash;
  571. Object key;
  572. Object value;
  573. Entry next;
  574. Entry(int hash, Object key, Object value, Entry next) {
  575. this.hash = hash;
  576. this.key = key;
  577. this.value = value;
  578. this.next = next;
  579. }
  580. protected Object clone() {
  581. return new Entry(hash, key, value,
  582. (next==null ? null : (Entry)next.clone()));
  583. }
  584. // Map.Entry Ops
  585. public Object getKey() {
  586. return key;
  587. }
  588. public Object getValue() {
  589. return value;
  590. }
  591. public Object setValue(Object value) {
  592. Object oldValue = this.value;
  593. this.value = value;
  594. return oldValue;
  595. }
  596. public boolean equals(Object o) {
  597. if (!(o instanceof Map.Entry))
  598. return false;
  599. Map.Entry e = (Map.Entry)o;
  600. return (key==null ? e.getKey()==null : key.equals(e.getKey())) &&
  601. (value==null ? e.getValue()==null : value.equals(e.getValue()));
  602. }
  603. public int hashCode() {
  604. return hash ^ (value==null ? 0 : value.hashCode());
  605. }
  606. public String toString() {
  607. return key+"="+value;
  608. }
  609. }
  610. // Types of Iterators
  611. private static final int KEYS = 0;
  612. private static final int VALUES = 1;
  613. private static final int ENTRIES = 2;
  614. private static EmptyHashIterator emptyHashIterator
  615. = new EmptyHashIterator();
  616. private static class EmptyHashIterator implements Iterator {
  617. EmptyHashIterator() {
  618. }
  619. public boolean hasNext() {
  620. return false;
  621. }
  622. public Object next() {
  623. throw new NoSuchElementException();
  624. }
  625. public void remove() {
  626. throw new IllegalStateException();
  627. }
  628. }
  629. private class HashIterator implements Iterator {
  630. Entry[] table = HashMap.this.table;
  631. int index = table.length;
  632. Entry entry = null;
  633. Entry lastReturned = null;
  634. int type;
  635. /**
  636. * The modCount value that the iterator believes that the backing
  637. * List should have. If this expectation is violated, the iterator
  638. * has detected concurrent modification.
  639. */
  640. private int expectedModCount = modCount;
  641. HashIterator(int type) {
  642. this.type = type;
  643. }
  644. public boolean hasNext() {
  645. Entry e = entry;
  646. int i = index;
  647. Entry t[] = table;
  648. /* Use locals for faster loop iteration */
  649. while (e == null && i > 0)
  650. e = t[--i];
  651. entry = e;
  652. index = i;
  653. return e != null;
  654. }
  655. public Object next() {
  656. if (modCount != expectedModCount)
  657. throw new ConcurrentModificationException();
  658. Entry et = entry;
  659. int i = index;
  660. Entry t[] = table;
  661. /* Use locals for faster loop iteration */
  662. while (et == null && i > 0)
  663. et = t[--i];
  664. entry = et;
  665. index = i;
  666. if (et != null) {
  667. Entry e = lastReturned = entry;
  668. entry = e.next;
  669. return type == KEYS ? e.key : (type == VALUES ? e.value : e);
  670. }
  671. throw new NoSuchElementException();
  672. }
  673. public void remove() {
  674. if (lastReturned == null)
  675. throw new IllegalStateException();
  676. if (modCount != expectedModCount)
  677. throw new ConcurrentModificationException();
  678. Entry[] tab = HashMap.this.table;
  679. int index = (lastReturned.hash & 0x7FFFFFFF) % tab.length;
  680. for (Entry e = tab[index], prev = null; e != null;
  681. prev = e, e = e.next) {
  682. if (e == lastReturned) {
  683. modCount++;
  684. expectedModCount++;
  685. if (prev == null)
  686. tab[index] = e.next;
  687. else
  688. prev.next = e.next;
  689. count--;
  690. lastReturned = null;
  691. return;
  692. }
  693. }
  694. throw new ConcurrentModificationException();
  695. }
  696. }
  697. /**
  698. * Save the state of the <tt>HashMap</tt> instance to a stream (i.e.,
  699. * serialize it).
  700. *
  701. * @serialData The <i>capacity</i> of the HashMap (the length of the
  702. * bucket array) is emitted (int), followed by the
  703. * <i>size</i> of the HashMap (the number of key-value
  704. * mappings), followed by the key (Object) and value (Object)
  705. * for each key-value mapping represented by the HashMap
  706. * The key-value mappings are emitted in no particular order.
  707. */
  708. private void writeObject(java.io.ObjectOutputStream s)
  709. throws IOException
  710. {
  711. // Write out the threshold, loadfactor, and any hidden stuff
  712. s.defaultWriteObject();
  713. // Write out number of buckets
  714. s.writeInt(table.length);
  715. // Write out size (number of Mappings)
  716. s.writeInt(count);
  717. // Write out keys and values (alternating)
  718. for (int index = table.length-1; index >= 0; index--) {
  719. Entry entry = table[index];
  720. while (entry != null) {
  721. s.writeObject(entry.key);
  722. s.writeObject(entry.value);
  723. entry = entry.next;
  724. }
  725. }
  726. }
  727. private static final long serialVersionUID = 362498820763181265L;
  728. /**
  729. * Reconstitute the <tt>HashMap</tt> instance from a stream (i.e.,
  730. * deserialize it).
  731. */
  732. private void readObject(java.io.ObjectInputStream s)
  733. throws IOException, ClassNotFoundException
  734. {
  735. // Read in the threshold, loadfactor, and any hidden stuff
  736. s.defaultReadObject();
  737. // Read in number of buckets and allocate the bucket array;
  738. int numBuckets = s.readInt();
  739. table = new Entry[numBuckets];
  740. // Read in size (number of Mappings)
  741. int size = s.readInt();
  742. // Read the keys and values, and put the mappings in the HashMap
  743. for (int i=0; i<size; i++) {
  744. Object key = s.readObject();
  745. Object value = s.readObject();
  746. put(key, value);
  747. }
  748. }
  749. int capacity() {
  750. return table.length;
  751. }
  752. float loadFactor() {
  753. return loadFactor;
  754. }
  755. }