blob: aba39c2cc3015d5edcaabd9ae53b3189137f208f [file] [log] [blame]
J. Duke319a3b92007-12-01 00:00:00 +00001/*
2 * Copyright 1998-2000 Sun Microsystems, Inc. All Rights Reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any questions.
24 */
25package com.sun.tools.jdi;
26
27import java.io.*;
28import java.util.*;
29
30/**
31 * Hash table based implementation of the Map interface. This implementation
32 * provides all of the optional Map operations, and permits null values and
33 * the null key. (HashMap is roughly equivalent to Hashtable, except that it
34 * is unsynchronized and permits nulls.) In addition, elements in the map are
35 * ordered and doubly linked together.
36 * <p>
37 * This implementation provides constant-time performance for the basic
38 * operations (get and put), assuming the the hash function disperses the
39 * elements properly among the buckets. Iteration over Collection views
40 * requires time proportional to its size (the number of key-value mappings)
41 * and returns elements in the order they are linked. In a HashMap the
42 * iteration would require time proportional to the capacity of the map
43 * plus the map size.
44 * <p>
45 * An instance of LinkedHashMap has two parameters that affect its efficiency:
46 * its <i>capacity</i> and its <i>load factor</i>. The load factor should be
47 * between 0.0 and 1.0. When the number of mappings in the LinkedHashMap exceeds
48 * the product of the load factor and the current capacity, the capacity is
49 * increased by calling the rehash method which requires time proportional
50 * to the number of key-value mappings in the map. Larger load factors
51 * use memory more efficiently, at the expense of larger expected time per
52 * lookup.
53 * <p>
54 * If many mappings are to be stored in a LinkedHashMap, creating it with a
55 * sufficiently large capacity will allow the mappings to be stored more
56 * efficiently than letting it perform automatic rehashing as needed to grow
57 * the table.
58 * <p>
59 * <strong>Note that this implementation is not synchronized.</strong> If
60 * multiple threads access a LinkedHashMap concurrently, and at least one of the
61 * threads modifies the LinkedHashMap structurally, it <em>must</em> be
62 * synchronized externally. (A structural modification is any operation that
63 * adds or deletes one or more mappings; merely changing the value associated
64 * with a key that is already contained in the Table is not a structural
65 * modification.) This is typically accomplished by synchronizing on some
66 * object that naturally encapsulates the LinkedHashMap. If no such object
67 * exists, the LinkedHashMap should be "wrapped" using the
68 * Collections.synchronizedSet method. This is best done at creation time, to
69 * prevent accidental unsynchronized access to the LinkedHashMap:
70 * <pre>
71 * Map m = Collections.synchronizedMap(new LinkedHashMap(...));
72 * </pre>
73 * <p>
74 * The Iterators returned by the iterator methods of the Collections returned
75 * by all of LinkedHashMap's "collection view methods" are <em>fail-fast</em>:
76 * if the LinkedHashMap is structurally modified at any time after the Iterator
77 * is created, in any way except through the Iterator's own remove or add
78 * methods, the Iterator will throw a ConcurrentModificationException. Thus,
79 * in the face of concurrent modification, the Iterator fails quickly and
80 * cleanly, rather than risking arbitrary, non-deterministic behavior at an
81 * undetermined time in the future.
82 *
83 * @author Josh Bloch
84 * @author Arthur van Hoff
85 * @author Zhenghua Li
86 * @see Object#hashCode()
87 * @see java.util.Collection
88 * @see java.util.Map
89 * @see java.util.TreeMap
90 * @see java.util.Hashtable
91 * @see java.util.HashMap
92 */
93
94import java.io.Serializable;
95
96public class LinkedHashMap extends AbstractMap implements Map, Serializable {
97 /**
98 * The hash table data.
99 */
100 private transient Entry table[];
101
102 /**
103 * The head of the double linked list.
104 */
105 private transient Entry header;
106
107 /**
108 * The total number of mappings in the hash table.
109 */
110 private transient int count;
111
112 /**
113 * Rehashes the table when count exceeds this threshold.
114 */
115 private int threshold;
116
117 /**
118 * The load factor for the LinkedHashMap.
119 */
120 private float loadFactor;
121
122 /**
123 * The number of times this LinkedHashMap has been structurally modified
124 * Structural modifications are those that change the number of mappings in
125 * the LinkedHashMap or otherwise modify its internal structure (e.g.,
126 * rehash). This field is used to make iterators on Collection-views of
127 * the LinkedHashMap fail-fast. (See ConcurrentModificationException).
128 */
129 private transient int modCount = 0;
130
131 /**
132 * Constructs a new, empty LinkedHashMap with the specified initial
133 * capacity and the specified load factor.
134 *
135 * @param initialCapacity the initial capacity of the LinkedHashMap.
136 * @param loadFactor a number between 0.0 and 1.0.
137 * @exception IllegalArgumentException if the initial capacity is less
138 * than or equal to zero, or if the load factor is less than
139 * or equal to zero.
140 */
141 public LinkedHashMap(int initialCapacity, float loadFactor) {
142 if (initialCapacity < 0)
143 throw new IllegalArgumentException("Illegal Initial Capacity: "+
144 initialCapacity);
145 if ((loadFactor > 1) || (loadFactor <= 0))
146 throw new IllegalArgumentException("Illegal Load factor: "+
147 loadFactor);
148 if (initialCapacity==0)
149 initialCapacity = 1;
150 this.loadFactor = loadFactor;
151 table = new Entry[initialCapacity];
152 threshold = (int)(initialCapacity * loadFactor);
153 header = new Entry(-1, null, null, null);
154 header.before = header.after = header;
155 }
156
157 /**
158 * Constructs a new, empty LinkedHashMap with the specified initial capacity
159 * and default load factor.
160 *
161 * @param initialCapacity the initial capacity of the LinkedHashMap.
162 */
163 public LinkedHashMap(int initialCapacity) {
164 this(initialCapacity, 0.75f);
165 }
166
167 /**
168 * Constructs a new, empty LinkedHashMap with a default capacity and load
169 * factor.
170 */
171 public LinkedHashMap() {
172 this(101, 0.75f);
173 }
174
175 /**
176 * Constructs a new LinkedHashMap with the same mappings as the given
177 * Map. The LinkedHashMap is created with a capacity of thrice the number
178 * of mappings in the given Map or 11 (whichever is greater), and a
179 * default load factor.
180 */
181 public LinkedHashMap(Map t) {
182 this(Math.max(3*t.size(), 11), 0.75f);
183 putAll(t);
184 }
185
186 /**
187 * Returns the number of key-value mappings in this Map.
188 */
189 public int size() {
190 return count;
191 }
192
193 /**
194 * Returns true if this Map contains no key-value mappings.
195 */
196 public boolean isEmpty() {
197 return count == 0;
198 }
199
200 /**
201 * Returns true if this LinkedHashMap maps one or more keys to the specified
202 * value.
203 *
204 * @param value value whose presence in this Map is to be tested.
205 */
206 public boolean containsValue(Object value) {
207 if (value==null) {
208 for (Entry e = header.after; e != header; e = e.after)
209 if (e.value==null)
210 return true;
211 } else {
212 for (Entry e = header.after; e != header; e = e.after)
213 if (value.equals(e.value))
214 return true;
215 }
216 return false;
217 }
218
219 /**
220 * Returns true if this LinkedHashMap contains a mapping for the specified
221 * key.
222 *
223 * @param key key whose presence in this Map is to be tested.
224 */
225 public boolean containsKey(Object key) {
226 Entry tab[] = table;
227 if (key != null) {
228 int hash = key.hashCode();
229 int index = (hash & 0x7FFFFFFF) % tab.length;
230 for (Entry e = tab[index]; e != null; e = e.next)
231 if (e.hash==hash && e.key.equals(key))
232 return true;
233 } else {
234 for (Entry e = tab[0]; e != null; e = e.next)
235 if (e.key==null)
236 return true;
237 }
238
239 return false;
240 }
241
242 /**
243 * Returns the value to which this LinkedHashMap maps the specified key.
244 * Returns null if the LinkedHashMap contains no mapping for this key.
245 * A return value of null does not <em>necessarily</em> indicate that the
246 * LinkedHashMap contains no mapping for the key; it's also possible that
247 * the LinkedHashMap explicitly maps the key to null. The containsKey
248 * operation may be used to distinguish these two cases.
249 *
250 * @param key key whose associated value is to be returned.
251 */
252 public Object get(Object key) {
253 Entry e = getEntry(key);
254 return e==null ? null : e.value;
255 }
256
257 /**
258 * Returns the entry associated with the specified key in the LinkedHashMap.
259 * Returns null if the LinkedHashMap contains no mapping for this key.
260 */
261 private Entry getEntry(Object key) {
262 Entry tab[] = table;
263
264 if (key != null) {
265 int hash = key.hashCode();
266 int index = (hash & 0x7FFFFFFF) % tab.length;
267 for (Entry e = tab[index]; e != null; e = e.next)
268 if ((e.hash == hash) && e.key.equals(key))
269 return e;
270 } else {
271 for (Entry e = tab[0]; e != null; e = e.next)
272 if (e.key==null)
273 return e;
274 }
275
276 return null;
277 }
278
279 /**
280 * Rehashes the contents of the LinkedHashMap into a LinkedHashMap with a
281 * larger capacity. This method is called automatically when the
282 * number of keys in the LinkedHashMap exceeds this LinkedHashMap's capacity
283 * and load factor.
284 */
285 private void rehash() {
286 int oldCapacity = table.length;
287 Entry oldMap[] = table;
288
289 int newCapacity = oldCapacity * 2 + 1;
290 Entry newMap[] = new Entry[newCapacity];
291
292 modCount++;
293 threshold = (int)(newCapacity * loadFactor);
294 table = newMap;
295
296 for (Entry e = header.after; e != header; e = e.after) {
297 int index = (e.hash & 0x7FFFFFFF) % newCapacity;
298 e.next = newMap[index];
299 newMap[index] = e;
300 }
301 }
302
303 /**
304 * Remove an entry from the linked list.
305 */
306 private void listRemove(Entry entry) {
307 if (entry == null) {
308 return;
309 }
310 entry.before.after = entry.after;
311 entry.after.before = entry.before;
312 }
313
314 /**
315 * Add the specified entry before the specified existing entry to
316 * the linked list.
317 */
318 private void listAddBefore(Entry entry, Entry existEntry) {
319 entry.after = existEntry;
320 entry.before = existEntry.before;
321 entry.before.after = entry;
322 entry.after.before = entry;
323 }
324
325 /**
326 * Returns the position of the mapping for the specified key
327 * in the ordered map.
328 *
329 * @param key the specified key.
330 * @return index of the key mapping.
331 */
332 public int indexOf(Object key) {
333 int i = 0;
334 if (key == null) {
335 for (Entry e = header.after; e != header; e = e.after, i++)
336 if (e.key == null)
337 return i;
338 } else {
339 for (Entry e = header.after; e != header; e = e.after, i++)
340 if(key.equals(e.key))
341 return i;
342 }
343 return -1;
344 }
345
346 /**
347 * Associates the specified value with the specified key in this
348 * LinkedHashMap. If the LinkedHashMap previously contained a mapping for
349 * this key, the old value is replaced and the position of this mapping
350 * entry in the double linked list remains the same. Otherwise, a new
351 * mapping entry is created and inserted into the list before the specified
352 * existing mapping entry. The method returns the previous value associated
353 * with the specified key, or null if there was no mapping for key. A null
354 * return can also indicate that the LinkedHashMap previously associated
355 * null with the specified key.
356 */
357 private Object putAhead(Object key, Object value, Entry existEntry) {
358 // Makes sure the key is not already in the LinkedHashMap.
359 Entry tab[] = table;
360 int hash = 0;
361 int index = 0;
362
363 if (key != null) {
364 hash = key.hashCode();
365 index = (hash & 0x7FFFFFFF) % tab.length;
366 for (Entry e = tab[index] ; e != null ; e = e.next) {
367 if ((e.hash == hash) && e.key.equals(key)) {
368 Object old = e.value;
369 e.value = value;
370 return old;
371 }
372 }
373 } else {
374 for (Entry e = tab[0] ; e != null ; e = e.next) {
375 if (e.key == null) {
376 Object old = e.value;
377 e.value = value;
378 return old;
379 }
380 }
381 }
382
383 modCount++;
384 if (count >= threshold) {
385 // Rehash the table if the threshold is exceeded
386 rehash();
387 tab = table;
388 index = (hash & 0x7FFFFFFF) % tab.length;
389 }
390
391 // Creates the new entry.
392 Entry e = new Entry(hash, key, value, tab[index]);
393 tab[index] = e;
394 listAddBefore(e, existEntry);
395 count++;
396 return null;
397 }
398
399 /**
400 * Associates the specified value with the specified key in this
401 * LinkedHashMap and position the mapping at the specified index.
402 * If the LinkedHashMap previously contained a mapping for this key,
403 * the old value is replaced and the position of this mapping entry
404 * in the double linked list remains the same. Otherwise, a new mapping
405 * entry is created and inserted into the list at the specified
406 * position.
407 *
408 * @param index the position to put the key-value mapping.
409 * @param key key with which the specified value is to be associated.
410 * @param value value to be associated with the specified key.
411 * @return previous value associated with specified key, or null if there
412 * was no mapping for key. A null return can also indicate that
413 * the LinkedHashMap previously associated null with the specified
414 * key.
415 */
416 public Object put(int index, Object key, Object value) {
417 if (index < 0 || index > count)
418 throw new IndexOutOfBoundsException();
419 Entry e = header.after;
420 if (index == count)
421 return putAhead(key, value, header); //fast approach for append
422 else {
423 for (int i = 0; i < index; i++)
424 e = e.after;
425 return putAhead(key, value, e);
426 }
427 }
428
429
430 /**
431 * Associates the specified value with the specified key in this
432 * LinkedHashMap. If the LinkedHashMap previously contained a mapping for
433 * this key, the old value is replaced. The mapping entry is also appended
434 * to the end of the ordered linked list.
435 *
436 * @param key key with which the specified value is to be associated.
437 * @param value value to be associated with the specified key.
438 * @return previous value associated with specified key, or null if there
439 * was no mapping for key. A null return can also indicate that
440 * the LinkedHashMap previously associated null with the specified
441 * key.
442 */
443 public Object put(Object key, Object value) {
444 return putAhead(key, value, header);
445 }
446
447 /**
448 * Removes the mapping for this key from this LinkedHashMap if present.
449 * The mapping would also be removed from the double linked list.
450 *
451 * @param key key whose mapping is to be removed from the Map.
452 * @return previous value associated with specified key, or null if there
453 * was no mapping for key. A null return can also indicate that
454 * the LinkedHashMap previously associated null with the specified
455 * key.
456 */
457 public Object remove(Object key) {
458 Entry tab[] = table;
459
460 if (key != null) {
461 int hash = key.hashCode();
462 int index = (hash & 0x7FFFFFFF) % tab.length;
463
464 for (Entry e = tab[index], prev = null; e != null;
465 prev = e, e = e.next) {
466 if ((e.hash == hash) && e.key.equals(key)) {
467 modCount++;
468 if (prev != null)
469 prev.next = e.next;
470 else
471 tab[index] = e.next;
472
473 count--;
474 Object oldValue = e.value;
475 e.value = null;
476
477 listRemove(e);
478 return oldValue;
479 }
480 }
481 } else {
482 for (Entry e = tab[0], prev = null; e != null;
483 prev = e, e = e.next) {
484 if (e.key == null) {
485 modCount++;
486 if (prev != null)
487 prev.next = e.next;
488 else
489 tab[0] = e.next;
490
491 count--;
492 Object oldValue = e.value;
493 e.value = null;
494
495 listRemove(e);
496 return oldValue;
497 }
498 }
499 }
500
501 return null;
502 }
503
504 /**
505 * Copies all of the mappings from the specified Map to this LinkedHashMap
506 * These mappings will replace any mappings that this LinkedHashMap had for
507 * any of the keys currently in the specified Map.
508 *
509 * @param t Mappings to be stored in this Map.
510 */
511 public void putAll(Map t) {
512 Iterator i = t.entrySet().iterator();
513 while (i.hasNext()) {
514 Map.Entry e = (Map.Entry) i.next();
515 put(e.getKey(), e.getValue());
516 }
517 }
518
519 /**
520 * Removes all mappings from this LinkedHashMap.
521 */
522 public void clear() {
523 Entry tab[] = table;
524 modCount++;
525 for (int index = tab.length; --index >= 0; )
526 tab[index] = null;
527 count = 0;
528 header.before = header.after = header;
529 }
530
531 /**
532 * Returns a shallow copy of this LinkedHashMap. The keys and values
533 * themselves are not cloned.
534 */
535 public Object clone() {
536 return new LinkedHashMap(this);
537 }
538
539 // Views
540
541 private transient Set keySet = null;
542 private transient Set entries = null;
543 private transient Collection values = null;
544
545 /**
546 * Returns a Set view of the keys contained in this LinkedHashMap. The Set
547 * is backed by the LinkedHashMap, so changes to the LinkedHashMap are
548 * reflected in the Set, and vice-versa. The Set supports element removal,
549 * which removes the corresponding mapping from the LinkedHashMap, via the
550 * Iterator.remove, Set.remove, removeAll retainAll, and clear operations.
551 * It does not support the add or addAll operations.
552 */
553 public Set keySet() {
554 if (keySet == null) {
555 keySet = new AbstractSet() {
556 public Iterator iterator() {
557 return new HashIterator(KEYS);
558 }
559 public int size() {
560 return count;
561 }
562 public boolean contains(Object o) {
563 return containsKey(o);
564 }
565 public boolean remove(Object o) {
566 return LinkedHashMap.this.remove(o) != null;
567 }
568 public void clear() {
569 LinkedHashMap.this.clear();
570 }
571 };
572 }
573 return keySet;
574 }
575
576 /**
577 * Returns a Collection view of the values contained in this LinkedHashMap.
578 * The Collection is backed by the LinkedHashMap, so changes to the
579 * LinkedHashMap are reflected in the Collection, and vice-versa. The
580 * Collection supports element removal, which removes the corresponding
581 * mapping from the LinkedHashMap, via the Iterator.remove,
582 * Collection.remove, removeAll, retainAll and clear operations. It does
583 * not support the add or addAll operations.
584 */
585 public Collection values() {
586 if (values==null) {
587 values = new AbstractCollection() {
588 public Iterator iterator() {
589 return new HashIterator(VALUES);
590 }
591 public int size() {
592 return count;
593 }
594 public boolean contains(Object o) {
595 return containsValue(o);
596 }
597 public void clear() {
598 LinkedHashMap.this.clear();
599 }
600 };
601 }
602 return values;
603 }
604
605 /**
606 * Returns a Collection view of the mappings contained in this
607 * LinkedHashMap. Each element in the returned collection is a Map.Entry.
608 * The Collection is backed by the LinkedHashMap, so changes to the
609 * LinkedHashMap are reflected in the Collection, and vice-versa. The
610 * Collection supports element removal, which removes the corresponding
611 * mapping from the LinkedHashMap, via the Iterator.remove,
612 * Collection.remove, removeAll, retainAll and clear operations. It does
613 * not support the add or addAll operations.
614 *
615 * @see java.util.Map.Entry
616 */
617 public Set entrySet() {
618 if (entries==null) {
619 entries = new AbstractSet() {
620 public Iterator iterator() {
621 return new HashIterator(ENTRIES);
622 }
623
624 public boolean contains(Object o) {
625 if (!(o instanceof Map.Entry))
626 return false;
627 Map.Entry entry = (Map.Entry)o;
628 Object key = entry.getKey();
629 Entry tab[] = table;
630 int hash = (key==null ? 0 : key.hashCode());
631 int index = (hash & 0x7FFFFFFF) % tab.length;
632
633 for (Entry e = tab[index]; e != null; e = e.next)
634 if (e.hash==hash && e.equals(entry))
635 return true;
636 return false;
637 }
638
639 public boolean remove(Object o) {
640 if (!(o instanceof Map.Entry))
641 return false;
642 Map.Entry entry = (Map.Entry)o;
643 Object key = entry.getKey();
644 Entry tab[] = table;
645 int hash = (key==null ? 0 : key.hashCode());
646 int index = (hash & 0x7FFFFFFF) % tab.length;
647
648 for (Entry e = tab[index], prev = null; e != null;
649 prev = e, e = e.next) {
650 if (e.hash==hash && e.equals(entry)) {
651 modCount++;
652 if (prev != null)
653 prev.next = e.next;
654 else
655 tab[index] = e.next;
656
657 count--;
658 e.value = null;
659 listRemove(e);
660 return true;
661 }
662 }
663 return false;
664 }
665
666 public int size() {
667 return count;
668 }
669
670 public void clear() {
671 LinkedHashMap.this.clear();
672 }
673 };
674 }
675
676 return entries;
677 }
678
679 /**
680 * Compares the specified Object with this Map for equality.
681 * Returns true if the given object is also a LinkedHashMap and the two
682 * Maps represent the same mappings in the same order. More formally,
683 * two Maps <code>t1</code> and <code>t2</code> represent the same mappings
684 * if <code>t1.keySet().equals(t2.keySet())</code> and for every
685 * key <code>k</code> in <code>t1.keySet()</code>, <code>
686 * (t1.get(k)==null ? t2.get(k)==null : t1.get(k).equals(t2.get(k)))
687 * </code>.
688 * <p>
689 * This implementation first checks if the specified Object is this Map;
690 * if so it returns true. Then, it checks if the specified Object is
691 * a Map whose size is identical to the size of this Set; if not, it
692 * it returns false. If so, it iterates over this Map and the specified
693 * Map's entrySet() Collection, and checks that the specified Map contains
694 * each mapping that this Map contains at the same position. If the
695 * specified Map fails to contain such a mapping in the right order, false
696 * is returned. If the iteration completes, true is returned.
697 *
698 * @param o Object to be compared for equality with this Map.
699 * @return true if the specified Object is equal to this Map.
700 *
701 */
702 public boolean equals(Object o) {
703 if (o == this)
704 return true;
705
706 if (!(o instanceof LinkedHashMap))
707 return false;
708 LinkedHashMap t = (LinkedHashMap) o;
709 if (t.size() != size())
710 return false;
711
712 Iterator i1 = entrySet().iterator();
713 Iterator i2 = t.entrySet().iterator();
714
715 while (i1.hasNext()) {
716 Entry e1 = (Entry) i1.next();
717 Entry e2 = (Entry) i2.next();
718
719 Object key1 = e1.getKey();
720 Object value1 = e1.getValue();
721 Object key2 = e2.getKey();
722 Object value2 = e2.getValue();
723
724 if ((key1 == null ? key2 == null : key1.equals(key2)) &&
725 (value1 == null ? value2 == null : value1.equals(value2))) {
726 continue;
727 } else {
728 return false;
729 }
730 }
731 return true;
732 }
733
734 /**
735 * LinkedHashMap collision list entry.
736 */
737 private static class Entry implements Map.Entry {
738 int hash;
739 Object key;
740 Object value;
741 Entry next;
742
743 // These fields comprise the doubly linked list that is used for
744 // iteration.
745 Entry before, after;
746
747 Entry(int hash, Object key, Object value, Entry next) {
748 this.hash = hash;
749 this.key = key;
750 this.value = value;
751 this.next = next;
752 }
753
754 // Map.Entry Ops
755
756 public Object getKey() {
757 return key;
758 }
759
760 public Object getValue() {
761 return value;
762 }
763
764 public Object setValue(Object value) {
765 Object oldValue = this.value;
766 this.value = value;
767 return oldValue;
768 }
769
770 public boolean equals(Object o) {
771 if (!(o instanceof Map.Entry))
772 return false;
773 Map.Entry e = (Map.Entry)o;
774
775 return (key==null ? e.getKey()==null : key.equals(e.getKey())) &&
776 (value==null ? e.getValue()==null : value.equals(e.getValue()));
777 }
778
779 public int hashCode() {
780 return hash ^ (value==null ? 0 : value.hashCode());
781 }
782
783 public String toString() {
784 return key+"="+value;
785 }
786 }
787
788 // Types of Iterators
789 private static final int KEYS = 0;
790 private static final int VALUES = 1;
791 private static final int ENTRIES = 2;
792
793 private class HashIterator implements Iterator {
794 private Entry[] table = LinkedHashMap.this.table;
795 private Entry entry = null;
796 private Entry lastReturned = null;
797 private int type;
798
799 /**
800 * The modCount value that the iterator believes that the backing
801 * List should have. If this expectation is violated, the iterator
802 * has detected concurrent modification.
803 */
804 private int expectedModCount = modCount;
805
806 HashIterator(int type) {
807 this.type = type;
808 this.entry = LinkedHashMap.this.header.after;
809 }
810
811 public boolean hasNext() {
812 return entry != header;
813 }
814
815 public Object next() {
816 if (modCount != expectedModCount)
817 throw new ConcurrentModificationException();
818 if (entry == LinkedHashMap.this.header)
819 throw new NoSuchElementException();
820
821 Entry e = lastReturned = entry;
822 entry = e.after;
823 return type == KEYS ? e.key : (type == VALUES ? e.value : e);
824 }
825
826 public void remove() {
827 if (lastReturned == null)
828 throw new IllegalStateException();
829 if (modCount != expectedModCount)
830 throw new ConcurrentModificationException();
831
832 Entry[] tab = LinkedHashMap.this.table;
833 int index = (lastReturned.hash & 0x7FFFFFFF) % tab.length;
834
835 for (Entry e = tab[index], prev = null; e != null;
836 prev = e, e = e.next) {
837 if (e == lastReturned) {
838 modCount++;
839 expectedModCount++;
840 if (prev == null)
841 tab[index] = e.next;
842 else
843 prev.next = e.next;
844 count--;
845 listRemove(e);
846 lastReturned = null;
847 return;
848 }
849 }
850 throw new ConcurrentModificationException();
851 }
852 }
853
854 /**
855 * Save the state of the LinkedHashMap to a stream (i.e., serialize it).
856 * The objects will be written out in the order they are linked
857 * in the list.
858 */
859 private void writeObject(java.io.ObjectOutputStream s)
860 throws IOException
861 {
862 // Write out the threshold, loadfactor, and any hidden stuff
863 s.defaultWriteObject();
864
865 // Write out number of buckets
866 s.writeInt(table.length);
867
868 // Write out size (number of Mappings)
869 s.writeInt(count);
870
871 // Write out keys and values (alternating)
872 for (Entry e = header.after; e != header; e = e.after) {
873 s.writeObject(e.key);
874 s.writeObject(e.value);
875 }
876 }
877
878 /**
879 * Reconstitute the LinkedHashMap from a stream (i.e., deserialize it).
880 */
881 private void readObject(java.io.ObjectInputStream s)
882 throws IOException, ClassNotFoundException
883 {
884 // Read in the threshold, loadfactor, and any hidden stuff
885 s.defaultReadObject();
886
887 // Read in number of buckets and allocate the bucket array;
888 int numBuckets = s.readInt();
889 table = new Entry[numBuckets];
890 header = new Entry(-1, null, null, null);
891 header.before = header;
892 header.after = header;
893
894 // Read in size (number of Mappings)
895 int size = s.readInt();
896
897 // Read the keys and values, and put the mappings in the LinkedHashMap
898 for (int i=0; i<size; i++) {
899 Object key = s.readObject();
900 Object value = s.readObject();
901 put(key, value);
902 }
903 }
904}