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