1. /*
  2. * @(#)Hashtable.java 1.73 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. * This class implements a hashtable, which maps keys to values. Any
  11. * non-<code>null</code> object can be used as a key or as a value. <p>
  12. *
  13. * To successfully store and retrieve objects from a hashtable, the
  14. * objects used as keys must implement the <code>hashCode</code>
  15. * method and the <code>equals</code> method. <p>
  16. *
  17. * An instance of <code>Hashtable</code> has two parameters that affect its
  18. * performance: <i>initial capacity</i> and <i>load factor</i>. The
  19. * <i>capacity</i> is the number of <i>buckets</i> in the hash table, and the
  20. * <i>initial capacity</i> is simply the capacity at the time the hash table
  21. * is created. Note that the hash table is <i>open</i>: in the case a "hash
  22. * collision", a single bucket stores multiple entries, which must be searched
  23. * sequentially. The <i>load factor</i> is a measure of how full the hash
  24. * table is allowed to get before its capacity is automatically increased.
  25. * When the number of entries in the hashtable exceeds the product of the load
  26. * factor and the current capacity, the capacity is increased by calling the
  27. * <code>rehash</code> method.<p>
  28. *
  29. * Generally, the default load factor (.75) offers a good tradeoff between
  30. * time and space costs. Higher values decrease the space overhead but
  31. * increase the time cost to look up an entry (which is reflected in most
  32. * <tt>Hashtable</tt> operations, including <tt>get</tt> and <tt>put</tt>).<p>
  33. *
  34. * The initial capacity controls a tradeoff between wasted space and the
  35. * need for <code>rehash</code> operations, which are time-consuming.
  36. * No <code>rehash</code> operations will <i>ever</i> occur if the initial
  37. * capacity is greater than the maximum number of entries the
  38. * <tt>Hashtable</tt> will contain divided by its load factor. However,
  39. * setting the initial capacity too high can waste space.<p>
  40. *
  41. * If many entries are to be made into a <code>Hashtable</code>,
  42. * creating it with a sufficiently large capacity may allow the
  43. * entries to be inserted more efficiently than letting it perform
  44. * automatic rehashing as needed to grow the table. <p>
  45. *
  46. * This example creates a hashtable of numbers. It uses the names of
  47. * the numbers as keys:
  48. * <p><blockquote><pre>
  49. * Hashtable numbers = new Hashtable();
  50. * numbers.put("one", new Integer(1));
  51. * numbers.put("two", new Integer(2));
  52. * numbers.put("three", new Integer(3));
  53. * </pre></blockquote>
  54. * <p>
  55. * To retrieve a number, use the following code:
  56. * <p><blockquote><pre>
  57. * Integer n = (Integer)numbers.get("two");
  58. * if (n != null) {
  59. * System.out.println("two = " + n);
  60. * }
  61. * </pre></blockquote>
  62. * <p>
  63. * As of JDK1.2, this class has been retrofitted to implement Map,
  64. * so that it becomes a part of Java's collection framework. Unlike
  65. * the new collection implementations, Hashtable is synchronized.<p>
  66. *
  67. * The Iterators returned by the iterator and listIterator methods
  68. * of the Collections returned by all of Hashtable's "collection view methods"
  69. * are <em>fail-fast</em>: if the Hashtable is structurally modified
  70. * at any time after the Iterator is created, in any way except through the
  71. * Iterator's own remove or add methods, the Iterator will throw a
  72. * ConcurrentModificationException. 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 future.
  75. * The Enumerations returned by Hashtable's keys and values methods are
  76. * <em>not</em> fail-fast.
  77. *
  78. * @author Arthur van Hoff
  79. * @author Josh Bloch
  80. * @version 1.73, 11/29/01
  81. * @see Object#equals(java.lang.Object)
  82. * @see Object#hashCode()
  83. * @see Hashtable#rehash()
  84. * @see Collection
  85. * @see Map
  86. * @see HashMap
  87. * @see TreeMap
  88. * @since JDK1.0
  89. */
  90. public class Hashtable extends Dictionary implements Map, Cloneable,
  91. java.io.Serializable {
  92. /**
  93. * The hash table data.
  94. */
  95. private transient Entry table[];
  96. /**
  97. * The total number of entries in the hash table.
  98. */
  99. private transient int count;
  100. /**
  101. * The table is rehashed when its size exceeds this threshold. (The
  102. * value of this field is (int)(capacity * loadFactor).)
  103. *
  104. * @serial
  105. */
  106. private int threshold;
  107. /**
  108. * The load factor for the hashtable.
  109. *
  110. * @serial
  111. */
  112. private float loadFactor;
  113. /**
  114. * The number of times this Hashtable has been structurally modified
  115. * Structural modifications are those that change the number of entries in
  116. * the Hashtable or otherwise modify its internal structure (e.g.,
  117. * rehash). This field is used to make iterators on Collection-views of
  118. * the Hashtable fail-fast. (See ConcurrentModificationException).
  119. */
  120. private transient int modCount = 0;
  121. /** use serialVersionUID from JDK 1.0.2 for interoperability */
  122. private static final long serialVersionUID = 1421746759512286392L;
  123. /**
  124. * Constructs a new, empty hashtable with the specified initial
  125. * capacity and the specified load factor.
  126. *
  127. * @param initialCapacity the initial capacity of the hashtable.
  128. * @param loadFactor the load factor of the hashtable.
  129. * @exception IllegalArgumentException if the initial capacity is less
  130. * than zero, or if the load factor is nonpositive.
  131. */
  132. public Hashtable(int initialCapacity, float loadFactor) {
  133. if (initialCapacity < 0)
  134. throw new IllegalArgumentException("Illegal Capacity: "+
  135. initialCapacity);
  136. if (loadFactor <= 0)
  137. throw new IllegalArgumentException("Illegal Load: "+loadFactor);
  138. if (initialCapacity==0)
  139. initialCapacity = 1;
  140. this.loadFactor = loadFactor;
  141. table = new Entry[initialCapacity];
  142. threshold = (int)(initialCapacity * loadFactor);
  143. }
  144. /**
  145. * Constructs a new, empty hashtable with the specified initial capacity
  146. * and default load factor, which is <tt>0.75</tt>.
  147. *
  148. * @param initialCapacity the initial capacity of the hashtable.
  149. * @exception IllegalArgumentException if the initial capacity is less
  150. * than zero.
  151. */
  152. public Hashtable(int initialCapacity) {
  153. this(initialCapacity, 0.75f);
  154. }
  155. /**
  156. * Constructs a new, empty hashtable with a default capacity and load
  157. * factor, which is <tt>0.75</tt>.
  158. */
  159. public Hashtable() {
  160. this(101, 0.75f);
  161. }
  162. /**
  163. * Constructs a new hashtable with the same mappings as the given
  164. * Map. The hashtable is created with a capacity of twice the number
  165. * of entries in the given Map or 11 (whichever is greater), and a
  166. * default load factor, which is <tt>0.75</tt>.
  167. *
  168. * @since JDK1.2
  169. */
  170. public Hashtable(Map t) {
  171. this(Math.max(2*t.size(), 11), 0.75f);
  172. putAll(t);
  173. }
  174. /**
  175. * Returns the number of keys in this hashtable.
  176. *
  177. * @return the number of keys in this hashtable.
  178. */
  179. public int size() {
  180. return count;
  181. }
  182. /**
  183. * Tests if this hashtable maps no keys to values.
  184. *
  185. * @return <code>true</code> if this hashtable maps no keys to values;
  186. * <code>false</code> otherwise.
  187. */
  188. public boolean isEmpty() {
  189. return count == 0;
  190. }
  191. /**
  192. * Returns an enumeration of the keys in this hashtable.
  193. *
  194. * @return an enumeration of the keys in this hashtable.
  195. * @see Enumeration
  196. * @see #elements()
  197. * @see #keySet()
  198. * @see Map
  199. */
  200. public synchronized Enumeration keys() {
  201. return new Enumerator(KEYS, false);
  202. }
  203. /**
  204. * Returns an enumeration of the values in this hashtable.
  205. * Use the Enumeration methods on the returned object to fetch the elements
  206. * sequentially.
  207. *
  208. * @return an enumeration of the values in this hashtable.
  209. * @see java.util.Enumeration
  210. * @see #keys()
  211. * @see #values()
  212. * @see Map
  213. */
  214. public synchronized Enumeration elements() {
  215. return new Enumerator(VALUES, false);
  216. }
  217. /**
  218. * Tests if some key maps into the specified value in this hashtable.
  219. * This operation is more expensive than the <code>containsKey</code>
  220. * method.<p>
  221. *
  222. * Note that this method is identical in functionality to containsValue,
  223. * (which is part of the Map interface in the collections framework).
  224. *
  225. * @param value a value to search for.
  226. * @return <code>true</code> if and only if some key maps to the
  227. * <code>value</code> argument in this hashtable as
  228. * determined by the <tt>equals</tt> method;
  229. * <code>false</code> otherwise.
  230. * @exception NullPointerException if the value is <code>null</code>.
  231. * @see #containsKey(Object)
  232. * @see #containsValue(Object)
  233. * @see Map
  234. */
  235. public synchronized boolean contains(Object value) {
  236. if (value == null) {
  237. throw new NullPointerException();
  238. }
  239. Entry tab[] = table;
  240. for (int i = tab.length ; i-- > 0 ;) {
  241. for (Entry e = tab[i] ; e != null ; e = e.next) {
  242. if (e.value.equals(value)) {
  243. return true;
  244. }
  245. }
  246. }
  247. return false;
  248. }
  249. /**
  250. * Returns true if this Hashtable maps one or more keys to this value.<p>
  251. *
  252. * Note that this method is identical in functionality to contains
  253. * (which predates the Map interface).
  254. *
  255. * @param value value whose presence in this Hashtable is to be tested.
  256. * @see Map
  257. * @since JDK1.2
  258. */
  259. public boolean containsValue(Object value) {
  260. return contains(value);
  261. }
  262. /**
  263. * Tests if the specified object is a key in this hashtable.
  264. *
  265. * @param key possible key.
  266. * @return <code>true</code> if and only if the specified object
  267. * is a key in this hashtable, as determined by the
  268. * <tt>equals</tt> method; <code>false</code> otherwise.
  269. * @see #contains(Object)
  270. */
  271. public synchronized boolean containsKey(Object key) {
  272. Entry tab[] = table;
  273. int hash = key.hashCode();
  274. int index = (hash & 0x7FFFFFFF) % tab.length;
  275. for (Entry e = tab[index] ; e != null ; e = e.next) {
  276. if ((e.hash == hash) && e.key.equals(key)) {
  277. return true;
  278. }
  279. }
  280. return false;
  281. }
  282. /**
  283. * Returns the value to which the specified key is mapped in this hashtable.
  284. *
  285. * @param key a key in the hashtable.
  286. * @return the value to which the key is mapped in this hashtable;
  287. * <code>null</code> if the key is not mapped to any value in
  288. * this hashtable.
  289. * @see #put(Object, Object)
  290. */
  291. public synchronized Object get(Object key) {
  292. Entry tab[] = table;
  293. int hash = key.hashCode();
  294. int index = (hash & 0x7FFFFFFF) % tab.length;
  295. for (Entry e = tab[index] ; e != null ; e = e.next) {
  296. if ((e.hash == hash) && e.key.equals(key)) {
  297. return e.value;
  298. }
  299. }
  300. return null;
  301. }
  302. /**
  303. * Increases the capacity of and internally reorganizes this
  304. * hashtable, in order to accommodate and access its entries more
  305. * efficiently. This method is called automatically when the
  306. * number of keys in the hashtable exceeds this hashtable's capacity
  307. * and load factor.
  308. */
  309. protected void rehash() {
  310. int oldCapacity = table.length;
  311. Entry oldMap[] = table;
  312. int newCapacity = oldCapacity * 2 + 1;
  313. Entry newMap[] = new Entry[newCapacity];
  314. modCount++;
  315. threshold = (int)(newCapacity * loadFactor);
  316. table = newMap;
  317. for (int i = oldCapacity ; i-- > 0 ;) {
  318. for (Entry old = oldMap[i] ; old != null ; ) {
  319. Entry e = old;
  320. old = old.next;
  321. int index = (e.hash & 0x7FFFFFFF) % newCapacity;
  322. e.next = newMap[index];
  323. newMap[index] = e;
  324. }
  325. }
  326. }
  327. /**
  328. * Maps the specified <code>key</code> to the specified
  329. * <code>value</code> in this hashtable. Neither the key nor the
  330. * value can be <code>null</code>. <p>
  331. *
  332. * The value can be retrieved by calling the <code>get</code> method
  333. * with a key that is equal to the original key.
  334. *
  335. * @param key the hashtable key.
  336. * @param value the value.
  337. * @return the previous value of the specified key in this hashtable,
  338. * or <code>null</code> if it did not have one.
  339. * @exception NullPointerException if the key or value is
  340. * <code>null</code>.
  341. * @see Object#equals(Object)
  342. * @see #get(Object)
  343. */
  344. public synchronized Object put(Object key, Object value) {
  345. // Make sure the value is not null
  346. if (value == null) {
  347. throw new NullPointerException();
  348. }
  349. // Makes sure the key is not already in the hashtable.
  350. Entry tab[] = table;
  351. int hash = key.hashCode();
  352. int index = (hash & 0x7FFFFFFF) % tab.length;
  353. for (Entry e = tab[index] ; e != null ; e = e.next) {
  354. if ((e.hash == hash) && e.key.equals(key)) {
  355. Object old = e.value;
  356. e.value = value;
  357. return old;
  358. }
  359. }
  360. modCount++;
  361. if (count >= threshold) {
  362. // Rehash the table if the threshold is exceeded
  363. rehash();
  364. tab = table;
  365. index = (hash & 0x7FFFFFFF) % tab.length;
  366. }
  367. // Creates the new entry.
  368. Entry e = new Entry(hash, key, value, tab[index]);
  369. tab[index] = e;
  370. count++;
  371. return null;
  372. }
  373. /**
  374. * Removes the key (and its corresponding value) from this
  375. * hashtable. This method does nothing if the key is not in the hashtable.
  376. *
  377. * @param key the key that needs to be removed.
  378. * @return the value to which the key had been mapped in this hashtable,
  379. * or <code>null</code> if the key did not have a mapping.
  380. */
  381. public synchronized Object remove(Object key) {
  382. Entry tab[] = table;
  383. int hash = key.hashCode();
  384. int index = (hash & 0x7FFFFFFF) % tab.length;
  385. for (Entry e = tab[index], prev = null ; e != null ; prev = e, e = e.next) {
  386. if ((e.hash == hash) && e.key.equals(key)) {
  387. modCount++;
  388. if (prev != null) {
  389. prev.next = e.next;
  390. } else {
  391. tab[index] = e.next;
  392. }
  393. count--;
  394. Object oldValue = e.value;
  395. e.value = null;
  396. return oldValue;
  397. }
  398. }
  399. return null;
  400. }
  401. /**
  402. * Copies all of the mappings from the specified Map to this Hashtable
  403. * These mappings will replace any mappings that this Hashtable had for any
  404. * of the keys currently in the specified Map.
  405. *
  406. * @since JDK1.2
  407. */
  408. public synchronized void putAll(Map t) {
  409. Iterator i = t.entrySet().iterator();
  410. while (i.hasNext()) {
  411. Map.Entry e = (Map.Entry) i.next();
  412. put(e.getKey(), e.getValue());
  413. }
  414. }
  415. /**
  416. * Clears this hashtable so that it contains no keys.
  417. */
  418. public synchronized void clear() {
  419. Entry tab[] = table;
  420. modCount++;
  421. for (int index = tab.length; --index >= 0; )
  422. tab[index] = null;
  423. count = 0;
  424. }
  425. /**
  426. * Creates a shallow copy of this hashtable. All the structure of the
  427. * hashtable itself is copied, but the keys and values are not cloned.
  428. * This is a relatively expensive operation.
  429. *
  430. * @return a clone of the hashtable.
  431. */
  432. public synchronized Object clone() {
  433. try {
  434. Hashtable t = (Hashtable)super.clone();
  435. t.table = new Entry[table.length];
  436. for (int i = table.length ; i-- > 0 ; ) {
  437. t.table[i] = (table[i] != null)
  438. ? (Entry)table[i].clone() : null;
  439. }
  440. t.keySet = null;
  441. t.entrySet = null;
  442. t.values = null;
  443. t.modCount = 0;
  444. return t;
  445. } catch (CloneNotSupportedException e) {
  446. // this shouldn't happen, since we are Cloneable
  447. throw new InternalError();
  448. }
  449. }
  450. /**
  451. * Returns a string representation of this <tt>Hashtable</tt> object
  452. * in the form of a set of entries, enclosed in braces and separated
  453. * by the ASCII characters "<tt>, </tt>" (comma and space). Each
  454. * entry is rendered as the key, an equals sign <tt>=</tt>, and the
  455. * associated element, where the <tt>toString</tt> method is used to
  456. * convert the key and element to strings. <p>Overrides to
  457. * <tt>toString</tt> method of <tt>Object</tt>.
  458. *
  459. * @return a string representation of this hashtable.
  460. */
  461. public synchronized String toString() {
  462. int max = size() - 1;
  463. StringBuffer buf = new StringBuffer();
  464. Iterator it = entrySet().iterator();
  465. buf.append("{");
  466. for (int i = 0; i <= max; i++) {
  467. Map.Entry e = (Map.Entry) (it.next());
  468. buf.append(e.getKey() + "=" + e.getValue());
  469. if (i < max)
  470. buf.append(", ");
  471. }
  472. buf.append("}");
  473. return buf.toString();
  474. }
  475. // Views
  476. private transient Set keySet = null;
  477. private transient Set entrySet = null;
  478. private transient Collection values = null;
  479. /**
  480. * Returns a Set view of the keys contained in this Hashtable. The Set
  481. * is backed by the Hashtable, so changes to the Hashtable are reflected
  482. * in the Set, and vice-versa. The Set supports element removal
  483. * (which removes the corresponding entry from the Hashtable), but not
  484. * element addition.
  485. *
  486. * @since JDK1.2
  487. */
  488. public Set keySet() {
  489. if (keySet == null)
  490. keySet = Collections.synchronizedSet(new KeySet(), this);
  491. return keySet;
  492. }
  493. private class KeySet extends AbstractSet {
  494. public Iterator iterator() {
  495. return new Enumerator(KEYS, true);
  496. }
  497. public int size() {
  498. return count;
  499. }
  500. public boolean contains(Object o) {
  501. return containsKey(o);
  502. }
  503. public boolean remove(Object o) {
  504. return Hashtable.this.remove(o) != null;
  505. }
  506. public void clear() {
  507. Hashtable.this.clear();
  508. }
  509. }
  510. /**
  511. * Returns a Set view of the entries contained in this Hashtable.
  512. * Each element in this collection is a Map.Entry. The Set is
  513. * backed by the Hashtable, so changes to the Hashtable are reflected in
  514. * the Set, and vice-versa. The Set supports element removal
  515. * (which removes the corresponding entry from the Hashtable),
  516. * but not element addition.
  517. *
  518. * @see Map.Entry
  519. * @since JDK1.2
  520. */
  521. public Set entrySet() {
  522. if (entrySet==null)
  523. entrySet = Collections.synchronizedSet(new EntrySet(), this);
  524. return entrySet;
  525. }
  526. private class EntrySet extends AbstractSet {
  527. public Iterator iterator() {
  528. return new Enumerator(ENTRIES, true);
  529. }
  530. public boolean contains(Object o) {
  531. if (!(o instanceof Map.Entry))
  532. return false;
  533. Map.Entry entry = (Map.Entry)o;
  534. Object key = entry.getKey();
  535. Entry tab[] = table;
  536. int hash = key.hashCode();
  537. int index = (hash & 0x7FFFFFFF) % tab.length;
  538. for (Entry e = tab[index]; e != null; e = e.next)
  539. if (e.hash==hash && e.equals(entry))
  540. return true;
  541. return false;
  542. }
  543. public boolean remove(Object o) {
  544. if (!(o instanceof Map.Entry))
  545. return false;
  546. Map.Entry entry = (Map.Entry)o;
  547. Object key = entry.getKey();
  548. Entry tab[] = table;
  549. int hash = key.hashCode();
  550. int index = (hash & 0x7FFFFFFF) % tab.length;
  551. for (Entry e = tab[index], prev = null; e != null;
  552. prev = e, e = e.next) {
  553. if (e.hash==hash && e.equals(entry)) {
  554. modCount++;
  555. if (prev != null)
  556. prev.next = e.next;
  557. else
  558. tab[index] = e.next;
  559. count--;
  560. e.value = null;
  561. return true;
  562. }
  563. }
  564. return false;
  565. }
  566. public int size() {
  567. return count;
  568. }
  569. public void clear() {
  570. Hashtable.this.clear();
  571. }
  572. }
  573. /**
  574. * Returns a Collection view of the values contained in this Hashtable.
  575. * The Collection is backed by the Hashtable, so changes to the Hashtable
  576. * are reflected in the Collection, and vice-versa. The Collection
  577. * supports element removal (which removes the corresponding entry from
  578. * the Hashtable), but not element addition.
  579. *
  580. * @since JDK1.2
  581. */
  582. public Collection values() {
  583. if (values==null)
  584. values = Collections.synchronizedCollection(new ValueCollection(),
  585. this);
  586. return values;
  587. }
  588. private class ValueCollection extends AbstractCollection {
  589. public Iterator iterator() {
  590. return new Enumerator(VALUES, true);
  591. }
  592. public int size() {
  593. return count;
  594. }
  595. public boolean contains(Object o) {
  596. return containsValue(o);
  597. }
  598. public void clear() {
  599. Hashtable.this.clear();
  600. }
  601. }
  602. // Comparison and hashing
  603. /**
  604. * Compares the specified Object with this Map for equality,
  605. * as per the definition in the Map interface.
  606. *
  607. * @return true if the specified Object is equal to this Map.
  608. * @see Map#equals(Object)
  609. * @since JDK1.2
  610. */
  611. public synchronized boolean equals(Object o) {
  612. if (o == this)
  613. return true;
  614. if (!(o instanceof Map))
  615. return false;
  616. Map t = (Map) o;
  617. if (t.size() != size())
  618. return false;
  619. Iterator i = entrySet().iterator();
  620. while (i.hasNext()) {
  621. Map.Entry e = (Map.Entry) i.next();
  622. Object key = e.getKey();
  623. Object value = e.getValue();
  624. if (value == null) {
  625. if (!(t.get(key)==null && t.containsKey(key)))
  626. return false;
  627. } else {
  628. if (!value.equals(t.get(key)))
  629. return false;
  630. }
  631. }
  632. return true;
  633. }
  634. /**
  635. * Returns the hash code value for this Map as per the definition in the
  636. * Map interface.
  637. *
  638. * @see Map#hashCode()
  639. * @since JDK1.2
  640. */
  641. public synchronized int hashCode() {
  642. int h = 0;
  643. Iterator i = entrySet().iterator();
  644. while (i.hasNext())
  645. h += i.next().hashCode();
  646. return h;
  647. }
  648. /**
  649. * Save the state of the Hashtable to a stream (i.e., serialize it).
  650. *
  651. * @serialData The <i>capacity</i> of the Hashtable (the length of the
  652. * bucket array) is emitted (int), followed by the
  653. * <i>size</i> of the Hashtable (the number of key-value
  654. * mappings), followed by the key (Object) and value (Object)
  655. * for each key-value mapping represented by the Hashtable
  656. * The key-value mappings are emitted in no particular order.
  657. */
  658. private synchronized void writeObject(java.io.ObjectOutputStream s)
  659. throws IOException
  660. {
  661. // Write out the length, threshold, loadfactor
  662. s.defaultWriteObject();
  663. // Write out length, count of elements and then the key/value objects
  664. s.writeInt(table.length);
  665. s.writeInt(count);
  666. for (int index = table.length-1; index >= 0; index--) {
  667. Entry entry = table[index];
  668. while (entry != null) {
  669. s.writeObject(entry.key);
  670. s.writeObject(entry.value);
  671. entry = entry.next;
  672. }
  673. }
  674. }
  675. /**
  676. * Reconstitute the Hashtable from a stream (i.e., deserialize it).
  677. */
  678. private synchronized void readObject(java.io.ObjectInputStream s)
  679. throws IOException, ClassNotFoundException
  680. {
  681. // Read in the length, threshold, and loadfactor
  682. s.defaultReadObject();
  683. // Read the original length of the array and number of elements
  684. int origlength = s.readInt();
  685. int elements = s.readInt();
  686. // Compute new size with a bit of room 5% to grow but
  687. // No larger than the original size. Make the length
  688. // odd if it's large enough, this helps distribute the entries.
  689. // Guard against the length ending up zero, that's not valid.
  690. int length = (int)(elements * loadFactor) + (elements / 20) + 3;
  691. if (length > elements && (length & 1) == 0)
  692. length--;
  693. if (origlength > 0 && length > origlength)
  694. length = origlength;
  695. table = new Entry[length];
  696. count = 0;
  697. // Read the number of elements and then all the key/value objects
  698. for (; elements > 0; elements--) {
  699. Object key = s.readObject();
  700. Object value = s.readObject();
  701. put(key, value);
  702. }
  703. }
  704. /**
  705. * Hashtable collision list.
  706. */
  707. private static class Entry implements Map.Entry {
  708. int hash;
  709. Object key;
  710. Object value;
  711. Entry next;
  712. protected Entry(int hash, Object key, Object value, Entry next) {
  713. this.hash = hash;
  714. this.key = key;
  715. this.value = value;
  716. this.next = next;
  717. }
  718. protected Object clone() {
  719. return new Entry(hash, key, value,
  720. (next==null ? null : (Entry)next.clone()));
  721. }
  722. // Map.Entry Ops
  723. public Object getKey() {
  724. return key;
  725. }
  726. public Object getValue() {
  727. return value;
  728. }
  729. public Object setValue(Object value) {
  730. if (value == null)
  731. throw new NullPointerException();
  732. Object oldValue = this.value;
  733. this.value = value;
  734. return oldValue;
  735. }
  736. public boolean equals(Object o) {
  737. if (!(o instanceof Map.Entry))
  738. return false;
  739. Map.Entry e = (Map.Entry)o;
  740. return (key==null ? e.getKey()==null : key.equals(e.getKey())) &&
  741. (value==null ? e.getValue()==null : value.equals(e.getValue()));
  742. }
  743. public int hashCode() {
  744. return hash ^ (value==null ? 0 : value.hashCode());
  745. }
  746. public String toString() {
  747. return key.toString()+"="+value.toString();
  748. }
  749. }
  750. // Types of Enumerations/Iterations
  751. private static final int KEYS = 0;
  752. private static final int VALUES = 1;
  753. private static final int ENTRIES = 2;
  754. /**
  755. * A hashtable enumerator class. This class implements both the
  756. * Enumeration and Iterator interfaces, but individual instances
  757. * can be created with the Iterator methods disabled. This is necessary
  758. * to avoid unintentionally increasing the capabilities granted a user
  759. * by passing an Enumeration.
  760. */
  761. private class Enumerator implements Enumeration, Iterator {
  762. Entry[] table = Hashtable.this.table;
  763. int index = table.length;
  764. Entry entry = null;
  765. Entry lastReturned = null;
  766. int type;
  767. /**
  768. * Indicates whether this Enumerator is serving as an Iterator
  769. * or an Enumeration. (true -> Iterator).
  770. */
  771. boolean iterator;
  772. /**
  773. * The modCount value that the iterator believes that the backing
  774. * List should have. If this expectation is violated, the iterator
  775. * has detected concurrent modification.
  776. */
  777. private int expectedModCount = modCount;
  778. Enumerator(int type, boolean iterator) {
  779. this.type = type;
  780. this.iterator = iterator;
  781. }
  782. public boolean hasMoreElements() {
  783. while (entry==null && index>0)
  784. entry = table[--index];
  785. return entry != null;
  786. }
  787. public Object nextElement() {
  788. while (entry==null && index>0)
  789. entry = table[--index];
  790. if (entry != null) {
  791. Entry e = lastReturned = entry;
  792. entry = e.next;
  793. return type == KEYS ? e.key : (type == VALUES ? e.value : e);
  794. }
  795. throw new NoSuchElementException("Hashtable Enumerator");
  796. }
  797. // Iterator methods
  798. public boolean hasNext() {
  799. return hasMoreElements();
  800. }
  801. public Object next() {
  802. if (modCount != expectedModCount)
  803. throw new ConcurrentModificationException();
  804. return nextElement();
  805. }
  806. public void remove() {
  807. if (!iterator)
  808. throw new UnsupportedOperationException();
  809. if (lastReturned == null)
  810. throw new IllegalStateException("Hashtable Enumerator");
  811. if (modCount != expectedModCount)
  812. throw new ConcurrentModificationException();
  813. synchronized(Hashtable.this) {
  814. Entry[] tab = Hashtable.this.table;
  815. int index = (lastReturned.hash & 0x7FFFFFFF) % tab.length;
  816. for (Entry e = tab[index], prev = null; e != null;
  817. prev = e, e = e.next) {
  818. if (e == lastReturned) {
  819. modCount++;
  820. expectedModCount++;
  821. if (prev == null)
  822. tab[index] = e.next;
  823. else
  824. prev.next = e.next;
  825. count--;
  826. lastReturned = null;
  827. return;
  828. }
  829. }
  830. throw new ConcurrentModificationException();
  831. }
  832. }
  833. }
  834. }