source: trunk/src/gcc/libjava/java/util/Hashtable.java@ 154

Last change on this file since 154 was 2, checked in by bird, 23 years ago

Initial revision

  • Property cvs2svn:cvs-rev set to 1.1
  • Property svn:eol-style set to native
  • Property svn:executable set to *
File size: 33.4 KB
Line 
1/* Hashtable.java -- a class providing a basic hashtable data structure,
2 mapping Object --> Object
3 Copyright (C) 1998, 1999, 2000, 2001 Free Software Foundation, Inc.
4
5This file is part of GNU Classpath.
6
7GNU Classpath is free software; you can redistribute it and/or modify
8it under the terms of the GNU General Public License as published by
9the Free Software Foundation; either version 2, or (at your option)
10any later version.
11
12GNU Classpath is distributed in the hope that it will be useful, but
13WITHOUT ANY WARRANTY; without even the implied warranty of
14MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15General Public License for more details.
16
17You should have received a copy of the GNU General Public License
18along with GNU Classpath; see the file COPYING. If not, write to the
19Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
2002111-1307 USA.
21
22Linking this library statically or dynamically with other modules is
23making a combined work based on this library. Thus, the terms and
24conditions of the GNU General Public License cover the whole
25combination.
26
27As a special exception, the copyright holders of this library give you
28permission to link this library with independent modules to produce an
29executable, regardless of the license terms of these independent
30modules, and to copy and distribute the resulting executable under
31terms of your choice, provided that you also meet, for each linked
32independent module, the terms and conditions of the license of that
33module. An independent module is a module which is not derived from
34or based on this library. If you modify this library, you may extend
35this exception to your version of the library, but you are not
36obligated to do so. If you do not wish to do so, delete this
37exception statement from your version. */
38
39package java.util;
40
41import java.io.IOException;
42import java.io.Serializable;
43import java.io.ObjectInputStream;
44import java.io.ObjectOutputStream;
45
46// NOTE: This implementation is very similar to that of HashMap. If you fix
47// a bug in here, chances are you should make a similar change to the HashMap
48// code.
49
50/**
51 * A class which implements a hashtable data structure.
52 * <p>
53 *
54 * This implementation of Hashtable uses a hash-bucket approach. That is:
55 * linear probing and rehashing is avoided; instead, each hashed value maps
56 * to a simple linked-list which, in the best case, only has one node.
57 * Assuming a large enough table, low enough load factor, and / or well
58 * implemented hashCode() methods, Hashtable should provide O(1)
59 * insertion, deletion, and searching of keys. Hashtable is O(n) in
60 * the worst case for all of these (if all keys hash to the same bucket).
61 * <p>
62 *
63 * This is a JDK-1.2 compliant implementation of Hashtable. As such, it
64 * belongs, partially, to the Collections framework (in that it implements
65 * Map). For backwards compatibility, it inherits from the obsolete and
66 * utterly useless Dictionary class.
67 * <p>
68 *
69 * Being a hybrid of old and new, Hashtable has methods which provide redundant
70 * capability, but with subtle and even crucial differences.
71 * For example, one can iterate over various aspects of a Hashtable with
72 * either an Iterator (which is the JDK-1.2 way of doing things) or with an
73 * Enumeration. The latter can end up in an undefined state if the Hashtable
74 * changes while the Enumeration is open.
75 * <p>
76 *
77 * Unlike HashMap, Hashtable does not accept `null' as a key value. Also,
78 * all accesses are synchronized: in a single thread environment, this is
79 * expensive, but in a multi-thread environment, this saves you the effort
80 * of extra synchronization. However, the old-style enumerators are not
81 * synchronized, because they can lead to unspecified behavior even if
82 * they were synchronized. You have been warned.
83 * <p>
84 *
85 * The iterators are <i>fail-fast</i>, meaning that any structural
86 * modification, except for <code>remove()</code> called on the iterator
87 * itself, cause the iterator to throw a
88 * <code>ConcurrentModificationException</code> rather than exhibit
89 * non-deterministic behavior.
90 *
91 * @author Jon Zeppieri
92 * @author Warren Levy
93 * @author Bryce McKinlay
94 * @author Eric Blake <[email protected]>
95 * @see HashMap
96 * @see TreeMap
97 * @see IdentityHashMap
98 * @see LinkedHashMap
99 * @since 1.0
100 * @status updated to 1.4
101 */
102public class Hashtable extends Dictionary
103 implements Map, Cloneable, Serializable
104{
105 /** Default number of buckets. This is the value the JDK 1.3 uses. Some
106 * early documentation specified this value as 101. That is incorrect.
107 */
108 private static final int DEFAULT_CAPACITY = 11;
109
110 /** An "enum" of iterator types. */
111 // Package visible for use by nested classes.
112 static final int KEYS = 0,
113 VALUES = 1,
114 ENTRIES = 2;
115
116 /**
117 * The default load factor; this is explicitly specified by the spec.
118 */
119 private static final float DEFAULT_LOAD_FACTOR = 0.75f;
120
121 /**
122 * Compatible with JDK 1.0+.
123 */
124 private static final long serialVersionUID = 1421746759512286392L;
125
126 /**
127 * The rounded product of the capacity and the load factor; when the number
128 * of elements exceeds the threshold, the Hashtable calls
129 * <code>rehash()</code>.
130 * @serial
131 */
132 private int threshold;
133
134 /**
135 * Load factor of this Hashtable: used in computing the threshold.
136 * @serial
137 */
138 private final float loadFactor;
139
140 /**
141 * Array containing the actual key-value mappings.
142 */
143 // Package visible for use by nested classes.
144 transient HashEntry[] buckets;
145
146 /**
147 * Counts the number of modifications this Hashtable has undergone, used
148 * by Iterators to know when to throw ConcurrentModificationExceptions.
149 */
150 // Package visible for use by nested classes.
151 transient int modCount;
152
153 /**
154 * The size of this Hashtable: denotes the number of key-value pairs.
155 */
156 // Package visible for use by nested classes.
157 transient int size;
158
159 /**
160 * The cache for {@link #keySet()}.
161 */
162 private transient Set keys;
163
164 /**
165 * The cache for {@link #values()}.
166 */
167 private transient Collection values;
168
169 /**
170 * The cache for {@link #entrySet()}.
171 */
172 private transient Set entries;
173
174 /**
175 * Class to represent an entry in the hash table. Holds a single key-value
176 * pair. A Hashtable Entry is identical to a HashMap Entry, except that
177 * `null' is not allowed for keys and values.
178 */
179 private static final class HashEntry extends BasicMapEntry
180 {
181 /** The next entry in the linked list. */
182 HashEntry next;
183
184 /**
185 * Simple constructor.
186 * @param key the key, already guaranteed non-null
187 * @param value the value, already guaranteed non-null
188 */
189 HashEntry(Object key, Object value)
190 {
191 super(key, value);
192 }
193
194 /**
195 * Resets the value.
196 * @param newValue the new value
197 * @return the prior value
198 * @throws NullPointerException if <code>newVal</code> is null
199 */
200 public Object setValue(Object newVal)
201 {
202 if (newVal == null)
203 throw new NullPointerException();
204 return super.setValue(newVal);
205 }
206 }
207
208 /**
209 * Construct a new Hashtable with the default capacity (11) and the default
210 * load factor (0.75).
211 */
212 public Hashtable()
213 {
214 this(DEFAULT_CAPACITY, DEFAULT_LOAD_FACTOR);
215 }
216
217 /**
218 * Construct a new Hashtable from the given Map, with initial capacity
219 * the greater of the size of <code>m</code> or the default of 11.
220 * <p>
221 *
222 * Every element in Map m will be put into this new Hashtable.
223 *
224 * @param m a Map whose key / value pairs will be put into
225 * the new Hashtable. <b>NOTE: key / value pairs
226 * are not cloned in this constructor.</b>
227 * @throws NullPointerException if m is null, or if m contains a mapping
228 * to or from `null'.
229 * @since 1.2
230 */
231 public Hashtable(Map m)
232 {
233 this(Math.max(m.size() * 2, DEFAULT_CAPACITY), DEFAULT_LOAD_FACTOR);
234 putAllInternal(m);
235 }
236
237 /**
238 * Construct a new Hashtable with a specific inital capacity and
239 * default load factor of 0.75.
240 *
241 * @param initialCapacity the initial capacity of this Hashtable (&gt;= 0)
242 * @throws IllegalArgumentException if (initialCapacity &lt; 0)
243 */
244 public Hashtable(int initialCapacity)
245 {
246 this(initialCapacity, DEFAULT_LOAD_FACTOR);
247 }
248
249 /**
250 * Construct a new Hashtable with a specific initial capacity and
251 * load factor.
252 *
253 * @param initialCapacity the initial capacity (&gt;= 0)
254 * @param loadFactor the load factor (&gt; 0, not NaN)
255 * @throws IllegalArgumentException if (initialCapacity &lt; 0) ||
256 * ! (loadFactor &gt; 0.0)
257 */
258 public Hashtable(int initialCapacity, float loadFactor)
259 {
260 if (initialCapacity < 0)
261 throw new IllegalArgumentException("Illegal Capacity: "
262 + initialCapacity);
263 if (! (loadFactor > 0)) // check for NaN too
264 throw new IllegalArgumentException("Illegal Load: " + loadFactor);
265
266 if (initialCapacity == 0)
267 initialCapacity = 1;
268 buckets = new HashEntry[initialCapacity];
269 this.loadFactor = loadFactor;
270 threshold = (int) (initialCapacity * loadFactor);
271 }
272
273 /**
274 * Returns the number of key-value mappings currently in this hashtable.
275 * @return the size
276 */
277 public synchronized int size()
278 {
279 return size;
280 }
281
282 /**
283 * Returns true if there are no key-value mappings currently in this table.
284 * @return <code>size() == 0</code>
285 */
286 public synchronized boolean isEmpty()
287 {
288 return size == 0;
289 }
290
291 /**
292 * Return an enumeration of the keys of this table. There's no point
293 * in synchronizing this, as you have already been warned that the
294 * enumeration is not specified to be thread-safe.
295 *
296 * @return the keys
297 * @see #elements()
298 * @see #keySet()
299 */
300 public Enumeration keys()
301 {
302 return new Enumerator(KEYS);
303 }
304
305 /**
306 * Return an enumeration of the values of this table. There's no point
307 * in synchronizing this, as you have already been warned that the
308 * enumeration is not specified to be thread-safe.
309 *
310 * @return the values
311 * @see #keys()
312 * @see #values()
313 */
314 public Enumeration elements()
315 {
316 return new Enumerator(VALUES);
317 }
318
319 /**
320 * Returns true if this Hashtable contains a value <code>o</code>,
321 * such that <code>o.equals(value)</code>. This is the same as
322 * <code>containsValue()</code>, and is O(n).
323 * <p>
324 *
325 * @param value the value to search for in this Hashtable
326 * @return true if at least one key maps to the value
327 * @throws NullPointerException if <code>value</code> is null
328 * @see #containsValue(Object)
329 * @see #containsKey(Object)
330 */
331 public synchronized boolean contains(Object value)
332 {
333 return containsValue(value);
334 }
335
336 /**
337 * Returns true if this Hashtable contains a value <code>o</code>, such that
338 * <code>o.equals(value)</code>. This is the new API for the old
339 * <code>contains()</code>.
340 *
341 * @param value the value to search for in this Hashtable
342 * @return true if at least one key maps to the value
343 * @throws NullPointerException if <code>value</code> is null
344 * @see #contains(Object)
345 * @see #containsKey(Object)
346 * @since 1.2
347 */
348 public boolean containsValue(Object value)
349 {
350 for (int i = buckets.length - 1; i >= 0; i--)
351 {
352 HashEntry e = buckets[i];
353 while (e != null)
354 {
355 if (value.equals(e.value))
356 return true;
357 e = e.next;
358 }
359 }
360
361 // Must throw on null argument even if the table is empty
362 if (value == null)
363 throw new NullPointerException();
364
365 return false;
366 }
367
368 /**
369 * Returns true if the supplied object <code>equals()</code> a key
370 * in this Hashtable.
371 *
372 * @param key the key to search for in this Hashtable
373 * @return true if the key is in the table
374 * @throws NullPointerException if key is null
375 * @see #containsValue(Object)
376 */
377 public synchronized boolean containsKey(Object key)
378 {
379 int idx = hash(key);
380 HashEntry e = buckets[idx];
381 while (e != null)
382 {
383 if (key.equals(e.key))
384 return true;
385 e = e.next;
386 }
387 return false;
388 }
389
390 /**
391 * Return the value in this Hashtable associated with the supplied key,
392 * or <code>null</code> if the key maps to nothing.
393 *
394 * @param key the key for which to fetch an associated value
395 * @return what the key maps to, if present
396 * @throws NullPointerException if key is null
397 * @see #put(Object, Object)
398 * @see #containsKey(Object)
399 */
400 public synchronized Object get(Object key)
401 {
402 int idx = hash(key);
403 HashEntry e = buckets[idx];
404 while (e != null)
405 {
406 if (key.equals(e.key))
407 return e.value;
408 e = e.next;
409 }
410 return null;
411 }
412
413 /**
414 * Puts the supplied value into the Map, mapped by the supplied key.
415 * Neither parameter may be null. The value may be retrieved by any
416 * object which <code>equals()</code> this key.
417 *
418 * @param key the key used to locate the value
419 * @param value the value to be stored in the table
420 * @return the prior mapping of the key, or null if there was none
421 * @throws NullPointerException if key or value is null
422 * @see #get(Object)
423 * @see Object#equals(Object)
424 */
425 public synchronized Object put(Object key, Object value)
426 {
427 int idx = hash(key);
428 HashEntry e = buckets[idx];
429
430 // Check if value is null since it is not permitted.
431 if (value == null)
432 throw new NullPointerException();
433
434 while (e != null)
435 {
436 if (key.equals(e.key))
437 {
438 // Bypass e.setValue, since we already know value is non-null.
439 Object r = e.value;
440 e.value = value;
441 return r;
442 }
443 else
444 {
445 e = e.next;
446 }
447 }
448
449 // At this point, we know we need to add a new entry.
450 modCount++;
451 if (++size > threshold)
452 {
453 rehash();
454 // Need a new hash value to suit the bigger table.
455 idx = hash(key);
456 }
457
458 e = new HashEntry(key, value);
459
460 e.next = buckets[idx];
461 buckets[idx] = e;
462
463 return null;
464 }
465
466 /**
467 * Removes from the table and returns the value which is mapped by the
468 * supplied key. If the key maps to nothing, then the table remains
469 * unchanged, and <code>null</code> is returned.
470 *
471 * @param key the key used to locate the value to remove
472 * @return whatever the key mapped to, if present
473 */
474 public synchronized Object remove(Object key)
475 {
476 int idx = hash(key);
477 HashEntry e = buckets[idx];
478 HashEntry last = null;
479
480 while (e != null)
481 {
482 if (key.equals(e.key))
483 {
484 modCount++;
485 if (last == null)
486 buckets[idx] = e.next;
487 else
488 last.next = e.next;
489 size--;
490 return e.value;
491 }
492 last = e;
493 e = e.next;
494 }
495 return null;
496 }
497
498 /**
499 * Copies all elements of the given map into this hashtable. However, no
500 * mapping can contain null as key or value. If this table already has
501 * a mapping for a key, the new mapping replaces the current one.
502 *
503 * @param m the map to be hashed into this
504 * @throws NullPointerException if m is null, or contains null keys or values
505 */
506 public synchronized void putAll(Map m)
507 {
508 Iterator itr = m.entrySet().iterator();
509
510 for (int msize = m.size(); msize > 0; msize--)
511 {
512 Map.Entry e = (Map.Entry) itr.next();
513 // Optimize in case the Entry is one of our own.
514 if (e instanceof BasicMapEntry)
515 {
516 BasicMapEntry entry = (BasicMapEntry) e;
517 put(entry.key, entry.value);
518 }
519 else
520 {
521 put(e.getKey(), e.getValue());
522 }
523 }
524 }
525
526 /**
527 * Clears the hashtable so it has no keys. This is O(1).
528 */
529 public synchronized void clear()
530 {
531 if (size > 0)
532 {
533 modCount++;
534 Arrays.fill(buckets, null);
535 size = 0;
536 }
537 }
538
539 /**
540 * Returns a shallow clone of this Hashtable. The Map itself is cloned,
541 * but its contents are not. This is O(n).
542 *
543 * @return the clone
544 */
545 public synchronized Object clone()
546 {
547 Hashtable copy = null;
548 try
549 {
550 copy = (Hashtable) super.clone();
551 }
552 catch (CloneNotSupportedException x)
553 {
554 // This is impossible.
555 }
556 copy.buckets = new HashEntry[buckets.length];
557 copy.putAllInternal(this);
558 // Clear the caches.
559 copy.keys = null;
560 copy.values = null;
561 copy.entries = null;
562 return copy;
563 }
564
565 /**
566 * Converts this Hashtable to a String, surrounded by braces, and with
567 * key/value pairs listed with an equals sign between, separated by a
568 * comma and space. For example, <code>"{a=1, b=2}"</code>.<p>
569 *
570 * NOTE: if the <code>toString()</code> method of any key or value
571 * throws an exception, this will fail for the same reason.
572 *
573 * @return the string representation
574 */
575 public synchronized String toString()
576 {
577 // Since we are already synchronized, and entrySet().iterator()
578 // would repeatedly re-lock/release the monitor, we directly use the
579 // unsynchronized HashIterator instead.
580 Iterator entries = new HashIterator(ENTRIES);
581 StringBuffer r = new StringBuffer("{");
582 for (int pos = size; pos > 0; pos--)
583 {
584 r.append(entries.next());
585 if (pos > 1)
586 r.append(", ");
587 }
588 r.append("}");
589 return r.toString();
590 }
591
592 /**
593 * Returns a "set view" of this Hashtable's keys. The set is backed by
594 * the hashtable, so changes in one show up in the other. The set supports
595 * element removal, but not element addition. The set is properly
596 * synchronized on the original hashtable. Sun has not documented the
597 * proper interaction of null with this set, but has inconsistent behavior
598 * in the JDK. Therefore, in this implementation, contains, remove,
599 * containsAll, retainAll, removeAll, and equals just ignore a null key
600 * rather than throwing a {@link NullPointerException}.
601 *
602 * @return a set view of the keys
603 * @see #values()
604 * @see #entrySet()
605 * @since 1.2
606 */
607 public Set keySet()
608 {
609 if (keys == null)
610 {
611 // Create a synchronized AbstractSet with custom implementations of
612 // those methods that can be overridden easily and efficiently.
613 Set r = new AbstractSet()
614 {
615 public int size()
616 {
617 return size;
618 }
619
620 public Iterator iterator()
621 {
622 return new HashIterator(KEYS);
623 }
624
625 public void clear()
626 {
627 Hashtable.this.clear();
628 }
629
630 public boolean contains(Object o)
631 {
632 if (o == null)
633 return false;
634 return containsKey(o);
635 }
636
637 public boolean remove(Object o)
638 {
639 return Hashtable.this.remove(o) != null;
640 }
641 };
642 // We must specify the correct object to synchronize upon, hence the
643 // use of a non-public API
644 keys = new Collections.SynchronizedSet(this, r);
645 }
646 return keys;
647 }
648
649 /**
650 * Returns a "collection view" (or "bag view") of this Hashtable's values.
651 * The collection is backed by the hashtable, so changes in one show up
652 * in the other. The collection supports element removal, but not element
653 * addition. The collection is properly synchronized on the original
654 * hashtable. Sun has not documented the proper interaction of null with
655 * this set, but has inconsistent behavior in the JDK. Therefore, in this
656 * implementation, contains, remove, containsAll, retainAll, removeAll, and
657 * equals just ignore a null value rather than throwing a
658 * {@link NullPointerException}.
659 *
660 * @return a bag view of the values
661 * @see #keySet()
662 * @see #entrySet()
663 * @since 1.2
664 */
665 public Collection values()
666 {
667 if (values == null)
668 {
669 // We don't bother overriding many of the optional methods, as doing so
670 // wouldn't provide any significant performance advantage.
671 Collection r = new AbstractCollection()
672 {
673 public int size()
674 {
675 return size;
676 }
677
678 public Iterator iterator()
679 {
680 return new HashIterator(VALUES);
681 }
682
683 public void clear()
684 {
685 Hashtable.this.clear();
686 }
687 };
688 // We must specify the correct object to synchronize upon, hence the
689 // use of a non-public API
690 values = new Collections.SynchronizedCollection(this, r);
691 }
692 return values;
693 }
694
695 /**
696 * Returns a "set view" of this Hashtable's entries. The set is backed by
697 * the hashtable, so changes in one show up in the other. The set supports
698 * element removal, but not element addition. The set is properly
699 * synchronized on the original hashtable. Sun has not documented the
700 * proper interaction of null with this set, but has inconsistent behavior
701 * in the JDK. Therefore, in this implementation, contains, remove,
702 * containsAll, retainAll, removeAll, and equals just ignore a null entry,
703 * or an entry with a null key or value, rather than throwing a
704 * {@link NullPointerException}. However, calling entry.setValue(null)
705 * will fail.
706 * <p>
707 *
708 * Note that the iterators for all three views, from keySet(), entrySet(),
709 * and values(), traverse the hashtable in the same sequence.
710 *
711 * @return a set view of the entries
712 * @see #keySet()
713 * @see #values()
714 * @see Map.Entry
715 * @since 1.2
716 */
717 public Set entrySet()
718 {
719 if (entries == null)
720 {
721 // Create an AbstractSet with custom implementations of those methods
722 // that can be overridden easily and efficiently.
723 Set r = new AbstractSet()
724 {
725 public int size()
726 {
727 return size;
728 }
729
730 public Iterator iterator()
731 {
732 return new HashIterator(ENTRIES);
733 }
734
735 public void clear()
736 {
737 Hashtable.this.clear();
738 }
739
740 public boolean contains(Object o)
741 {
742 return getEntry(o) != null;
743 }
744
745 public boolean remove(Object o)
746 {
747 HashEntry e = getEntry(o);
748 if (e != null)
749 {
750 Hashtable.this.remove(e.key);
751 return true;
752 }
753 return false;
754 }
755 };
756 // We must specify the correct object to synchronize upon, hence the
757 // use of a non-public API
758 entries = new Collections.SynchronizedSet(this, r);
759 }
760 return entries;
761 }
762
763 /**
764 * Returns true if this Hashtable equals the supplied Object <code>o</code>.
765 * As specified by Map, this is:
766 * <pre>
767 * (o instanceof Map) && entrySet().equals(((Map) o).entrySet());
768 * </pre>
769 *
770 * @param o the object to compare to
771 * @return true if o is an equal map
772 * @since 1.2
773 */
774 public boolean equals(Object o)
775 {
776 // no need to synchronize, entrySet().equals() does that
777 if (o == this)
778 return true;
779 if (!(o instanceof Map))
780 return false;
781
782 return entrySet().equals(((Map) o).entrySet());
783 }
784
785 /**
786 * Returns the hashCode for this Hashtable. As specified by Map, this is
787 * the sum of the hashCodes of all of its Map.Entry objects
788 *
789 * @return the sum of the hashcodes of the entries
790 * @since 1.2
791 */
792 public synchronized int hashCode()
793 {
794 // Since we are already synchronized, and entrySet().iterator()
795 // would repeatedly re-lock/release the monitor, we directly use the
796 // unsynchronized HashIterator instead.
797 Iterator itr = new HashIterator(ENTRIES);
798 int hashcode = 0;
799 for (int pos = size; pos > 0; pos--)
800 hashcode += itr.next().hashCode();
801
802 return hashcode;
803 }
804
805 /**
806 * Helper method that returns an index in the buckets array for `key'
807 * based on its hashCode().
808 *
809 * @param key the key
810 * @return the bucket number
811 * @throws NullPointerException if key is null
812 */
813 private int hash(Object key)
814 {
815 return Math.abs(key.hashCode() % buckets.length);
816 }
817
818 /**
819 * Helper method for entrySet(), which matches both key and value
820 * simultaneously. Ignores null, as mentioned in entrySet().
821 *
822 * @param o the entry to match
823 * @return the matching entry, if found, or null
824 * @see #entrySet()
825 */
826 private HashEntry getEntry(Object o)
827 {
828 if (! (o instanceof Map.Entry))
829 return null;
830 Object key = ((Map.Entry) o).getKey();
831 if (key == null)
832 return null;
833
834 int idx = hash(key);
835 HashEntry e = buckets[idx];
836 while (e != null)
837 {
838 if (o.equals(e))
839 return e;
840 e = e.next;
841 }
842 return null;
843 }
844
845 /**
846 * A simplified, more efficient internal implementation of putAll(). The
847 * Map constructor and clone() should not call putAll or put, in order to
848 * be compatible with the JDK implementation with respect to subclasses.
849 *
850 * @param m the map to initialize this from
851 */
852 void putAllInternal(Map m)
853 {
854 Iterator itr = m.entrySet().iterator();
855 int msize = m.size();
856 this.size = msize;
857
858 for (; msize > 0; msize--)
859 {
860 Map.Entry e = (Map.Entry) itr.next();
861 Object key = e.getKey();
862 int idx = hash(key);
863 HashEntry he = new HashEntry(key, e.getValue());
864 he.next = buckets[idx];
865 buckets[idx] = he;
866 }
867 }
868
869 /**
870 * Increases the size of the Hashtable and rehashes all keys to new array
871 * indices; this is called when the addition of a new value would cause
872 * size() > threshold. Note that the existing Entry objects are reused in
873 * the new hash table.
874 * <p>
875 *
876 * This is not specified, but the new size is twice the current size plus
877 * one; this number is not always prime, unfortunately. This implementation
878 * is not synchronized, as it is only invoked from synchronized methods.
879 */
880 protected void rehash()
881 {
882 HashEntry[] oldBuckets = buckets;
883
884 int newcapacity = (buckets.length * 2) + 1;
885 threshold = (int) (newcapacity * loadFactor);
886 buckets = new HashEntry[newcapacity];
887
888 for (int i = oldBuckets.length - 1; i >= 0; i--)
889 {
890 HashEntry e = oldBuckets[i];
891 while (e != null)
892 {
893 int idx = hash(e.key);
894 HashEntry dest = buckets[idx];
895
896 if (dest != null)
897 {
898 while (dest.next != null)
899 dest = dest.next;
900 dest.next = e;
901 }
902 else
903 {
904 buckets[idx] = e;
905 }
906
907 HashEntry next = e.next;
908 e.next = null;
909 e = next;
910 }
911 }
912 }
913
914 /**
915 * Serializes this object to the given stream.
916 *
917 * @param s the stream to write to
918 * @throws IOException if the underlying stream fails
919 * @serialData the <i>capacity</i> (int) that is the length of the
920 * bucket array, the <i>size</i> (int) of the hash map
921 * are emitted first. They are followed by size entries,
922 * each consisting of a key (Object) and a value (Object).
923 */
924 private synchronized void writeObject(ObjectOutputStream s)
925 throws IOException
926 {
927 // Write the threshold and loadFactor fields.
928 s.defaultWriteObject();
929
930 s.writeInt(buckets.length);
931 s.writeInt(size);
932 // Since we are already synchronized, and entrySet().iterator()
933 // would repeatedly re-lock/release the monitor, we directly use the
934 // unsynchronized HashIterator instead.
935 Iterator it = new HashIterator(ENTRIES);
936 while (it.hasNext())
937 {
938 HashEntry entry = (HashEntry) it.next();
939 s.writeObject(entry.key);
940 s.writeObject(entry.value);
941 }
942 }
943
944 /**
945 * Deserializes this object from the given stream.
946 *
947 * @param s the stream to read from
948 * @throws ClassNotFoundException if the underlying stream fails
949 * @throws IOException if the underlying stream fails
950 * @serialData the <i>capacity</i> (int) that is the length of the
951 * bucket array, the <i>size</i> (int) of the hash map
952 * are emitted first. They are followed by size entries,
953 * each consisting of a key (Object) and a value (Object).
954 */
955 private void readObject(ObjectInputStream s)
956 throws IOException, ClassNotFoundException
957 {
958 // Read the threshold and loadFactor fields.
959 s.defaultReadObject();
960
961 // Read and use capacity.
962 buckets = new HashEntry[s.readInt()];
963 int len = s.readInt();
964
965 // Read and use key/value pairs.
966 // TODO: should we be defensive programmers, and check for illegal nulls?
967 while (--len >= 0)
968 put(s.readObject(), s.readObject());
969 }
970
971 /**
972 * A class which implements the Iterator interface and is used for
973 * iterating over Hashtables.
974 * This implementation is parameterized to give a sequential view of
975 * keys, values, or entries; it also allows the removal of elements,
976 * as per the Javasoft spec. Note that it is not synchronized; this is
977 * a performance enhancer since it is never exposed externally and is
978 * only used within synchronized blocks above.
979 *
980 * @author Jon Zeppieri
981 */
982 private final class HashIterator implements Iterator
983 {
984 /**
985 * The type of this Iterator: {@link #KEYS}, {@link #VALUES},
986 * or {@link #ENTRIES}.
987 */
988 final int type;
989 /**
990 * The number of modifications to the backing Hashtable that we know about.
991 */
992 int knownMod = modCount;
993 /** The number of elements remaining to be returned by next(). */
994 int count = size;
995 /** Current index in the physical hash table. */
996 int idx = buckets.length;
997 /** The last Entry returned by a next() call. */
998 HashEntry last;
999 /**
1000 * The next entry that should be returned by next(). It is set to something
1001 * if we're iterating through a bucket that contains multiple linked
1002 * entries. It is null if next() needs to find a new bucket.
1003 */
1004 HashEntry next;
1005
1006 /**
1007 * Construct a new HashIterator with the supplied type.
1008 * @param type {@link #KEYS}, {@link #VALUES}, or {@link #ENTRIES}
1009 */
1010 HashIterator(int type)
1011 {
1012 this.type = type;
1013 }
1014
1015 /**
1016 * Returns true if the Iterator has more elements.
1017 * @return true if there are more elements
1018 * @throws ConcurrentModificationException if the hashtable was modified
1019 */
1020 public boolean hasNext()
1021 {
1022 if (knownMod != modCount)
1023 throw new ConcurrentModificationException();
1024 return count > 0;
1025 }
1026
1027 /**
1028 * Returns the next element in the Iterator's sequential view.
1029 * @return the next element
1030 * @throws ConcurrentModificationException if the hashtable was modified
1031 * @throws NoSuchElementException if there is none
1032 */
1033 public Object next()
1034 {
1035 if (knownMod != modCount)
1036 throw new ConcurrentModificationException();
1037 if (count == 0)
1038 throw new NoSuchElementException();
1039 count--;
1040 HashEntry e = next;
1041
1042 while (e == null)
1043 e = buckets[--idx];
1044
1045 next = e.next;
1046 last = e;
1047 if (type == VALUES)
1048 return e.value;
1049 if (type == KEYS)
1050 return e.key;
1051 return e;
1052 }
1053
1054 /**
1055 * Removes from the backing Hashtable the last element which was fetched
1056 * with the <code>next()</code> method.
1057 * @throws ConcurrentModificationException if the hashtable was modified
1058 * @throws IllegalStateException if called when there is no last element
1059 */
1060 public void remove()
1061 {
1062 if (knownMod != modCount)
1063 throw new ConcurrentModificationException();
1064 if (last == null)
1065 throw new IllegalStateException();
1066
1067 Hashtable.this.remove(last.key);
1068 last = null;
1069 knownMod++;
1070 }
1071 } // class HashIterator
1072
1073
1074 /**
1075 * Enumeration view of this Hashtable, providing sequential access to its
1076 * elements; this implementation is parameterized to provide access either
1077 * to the keys or to the values in the Hashtable.
1078 *
1079 * <b>NOTE</b>: Enumeration is not safe if new elements are put in the table
1080 * as this could cause a rehash and we'd completely lose our place. Even
1081 * without a rehash, it is undetermined if a new element added would
1082 * appear in the enumeration. The spec says nothing about this, but
1083 * the "Java Class Libraries" book infers that modifications to the
1084 * hashtable during enumeration causes indeterminate results. Don't do it!
1085 *
1086 * @author Jon Zeppieri
1087 */
1088 private final class Enumerator implements Enumeration
1089 {
1090 /**
1091 * The type of this Iterator: {@link #KEYS} or {@link #VALUES}.
1092 */
1093 final int type;
1094 /** The number of elements remaining to be returned by next(). */
1095 int count = size;
1096 /** Current index in the physical hash table. */
1097 int idx = buckets.length;
1098 /**
1099 * Entry which will be returned by the next nextElement() call. It is
1100 * set if we are iterating through a bucket with multiple entries, or null
1101 * if we must look in the next bucket.
1102 */
1103 HashEntry next;
1104
1105 /**
1106 * Construct the enumeration.
1107 * @param type either {@link #KEYS} or {@link #VALUES}.
1108 */
1109 Enumerator(int type)
1110 {
1111 this.type = type;
1112 }
1113
1114 /**
1115 * Checks whether more elements remain in the enumeration.
1116 * @return true if nextElement() will not fail.
1117 */
1118 public boolean hasMoreElements()
1119 {
1120 return count > 0;
1121 }
1122
1123 /**
1124 * Returns the next element.
1125 * @return the next element
1126 * @throws NoSuchElementException if there is none.
1127 */
1128 public Object nextElement()
1129 {
1130 if (count == 0)
1131 throw new NoSuchElementException("Hashtable Enumerator");
1132 count--;
1133 HashEntry e = next;
1134
1135 while (e == null)
1136 e = buckets[--idx];
1137
1138 next = e.next;
1139 return type == VALUES ? e.value : e.key;
1140 }
1141 } // class Enumerator
1142}
Note: See TracBrowser for help on using the repository browser.