blob: 4c76a74e93ad43b0f93b599086b1d1e99c912485 [file] [log] [blame]
Alan Viverette3da604b2020-06-10 18:34:39 +00001/*
2 * Copyright (C) 2014 The Android Open Source Project
3 * Copyright (c) 1994, 2013, Oracle and/or its affiliates. All rights reserved.
4 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 *
6 * This code is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License version 2 only, as
8 * published by the Free Software Foundation. Oracle designates this
9 * particular file as subject to the "Classpath" exception as provided
10 * by Oracle in the LICENSE file that accompanied this code.
11 *
12 * This code is distributed in the hope that it will be useful, but WITHOUT
13 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
15 * version 2 for more details (a copy is included in the LICENSE file that
16 * accompanied this code).
17 *
18 * You should have received a copy of the GNU General Public License version
19 * 2 along with this work; if not, write to the Free Software Foundation,
20 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
21 *
22 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
23 * or visit www.oracle.com if you need additional information or have any
24 * questions.
25 */
26
27package java.util;
28
29import java.io.*;
30import java.util.function.BiConsumer;
31import java.util.function.BiFunction;
32import java.util.function.Function;
33
34/**
35 * This class implements a hash table, which maps keys to values. Any
36 * non-<code>null</code> object can be used as a key or as a value. <p>
37 *
38 * To successfully store and retrieve objects from a hashtable, the
39 * objects used as keys must implement the <code>hashCode</code>
40 * method and the <code>equals</code> method. <p>
41 *
42 * An instance of <code>Hashtable</code> has two parameters that affect its
43 * performance: <i>initial capacity</i> and <i>load factor</i>. The
44 * <i>capacity</i> is the number of <i>buckets</i> in the hash table, and the
45 * <i>initial capacity</i> is simply the capacity at the time the hash table
46 * is created. Note that the hash table is <i>open</i>: in the case of a "hash
47 * collision", a single bucket stores multiple entries, which must be searched
48 * sequentially. The <i>load factor</i> is a measure of how full the hash
49 * table is allowed to get before its capacity is automatically increased.
50 * The initial capacity and load factor parameters are merely hints to
51 * the implementation. The exact details as to when and whether the rehash
52 * method is invoked are implementation-dependent.<p>
53 *
54 * Generally, the default load factor (.75) offers a good tradeoff between
55 * time and space costs. Higher values decrease the space overhead but
56 * increase the time cost to look up an entry (which is reflected in most
57 * <tt>Hashtable</tt> operations, including <tt>get</tt> and <tt>put</tt>).<p>
58 *
59 * The initial capacity controls a tradeoff between wasted space and the
60 * need for <code>rehash</code> operations, which are time-consuming.
61 * No <code>rehash</code> operations will <i>ever</i> occur if the initial
62 * capacity is greater than the maximum number of entries the
63 * <tt>Hashtable</tt> will contain divided by its load factor. However,
64 * setting the initial capacity too high can waste space.<p>
65 *
66 * If many entries are to be made into a <code>Hashtable</code>,
67 * creating it with a sufficiently large capacity may allow the
68 * entries to be inserted more efficiently than letting it perform
69 * automatic rehashing as needed to grow the table. <p>
70 *
71 * This example creates a hashtable of numbers. It uses the names of
72 * the numbers as keys:
73 * <pre> {@code
74 * Hashtable<String, Integer> numbers
75 * = new Hashtable<String, Integer>();
76 * numbers.put("one", 1);
77 * numbers.put("two", 2);
78 * numbers.put("three", 3);}</pre>
79 *
80 * <p>To retrieve a number, use the following code:
81 * <pre> {@code
82 * Integer n = numbers.get("two");
83 * if (n != null) {
84 * System.out.println("two = " + n);
85 * }}</pre>
86 *
87 * <p>The iterators returned by the <tt>iterator</tt> method of the collections
88 * returned by all of this class's "collection view methods" are
89 * <em>fail-fast</em>: if the Hashtable is structurally modified at any time
90 * after the iterator is created, in any way except through the iterator's own
91 * <tt>remove</tt> method, the iterator will throw a {@link
92 * ConcurrentModificationException}. Thus, in the face of concurrent
93 * modification, the iterator fails quickly and cleanly, rather than risking
94 * arbitrary, non-deterministic behavior at an undetermined time in the future.
95 * The Enumerations returned by Hashtable's keys and elements methods are
96 * <em>not</em> fail-fast.
97 *
98 * <p>Note that the fail-fast behavior of an iterator cannot be guaranteed
99 * as it is, generally speaking, impossible to make any hard guarantees in the
100 * presence of unsynchronized concurrent modification. Fail-fast iterators
101 * throw <tt>ConcurrentModificationException</tt> on a best-effort basis.
102 * Therefore, it would be wrong to write a program that depended on this
103 * exception for its correctness: <i>the fail-fast behavior of iterators
104 * should be used only to detect bugs.</i>
105 *
106 * <p>As of the Java 2 platform v1.2, this class was retrofitted to
107 * implement the {@link Map} interface, making it a member of the
108 * <a href="{@docRoot}/../technotes/guides/collections/index.html">
109 *
110 * Java Collections Framework</a>. Unlike the new collection
111 * implementations, {@code Hashtable} is synchronized. If a
112 * thread-safe implementation is not needed, it is recommended to use
113 * {@link HashMap} in place of {@code Hashtable}. If a thread-safe
114 * highly-concurrent implementation is desired, then it is recommended
115 * to use {@link java.util.concurrent.ConcurrentHashMap} in place of
116 * {@code Hashtable}.
117 *
118 * @author Arthur van Hoff
119 * @author Josh Bloch
120 * @author Neal Gafter
121 * @see Object#equals(java.lang.Object)
122 * @see Object#hashCode()
123 * @see Hashtable#rehash()
124 * @see Collection
125 * @see Map
126 * @see HashMap
127 * @see TreeMap
128 * @since JDK1.0
129 */
130public class Hashtable<K,V>
131 extends Dictionary<K,V>
132 implements Map<K,V>, Cloneable, java.io.Serializable {
133
134 /**
135 * The hash table data.
136 */
137 private transient HashtableEntry<?,?>[] table;
138
139 /**
140 * The total number of entries in the hash table.
141 */
142 private transient int count;
143
144 /**
145 * The table is rehashed when its size exceeds this threshold. (The
146 * value of this field is (int)(capacity * loadFactor).)
147 *
148 * @serial
149 */
150 private int threshold;
151
152 /**
153 * The load factor for the hashtable.
154 *
155 * @serial
156 */
157 private float loadFactor;
158
159 /**
160 * The number of times this Hashtable has been structurally modified
161 * Structural modifications are those that change the number of entries in
162 * the Hashtable or otherwise modify its internal structure (e.g.,
163 * rehash). This field is used to make iterators on Collection-views of
164 * the Hashtable fail-fast. (See ConcurrentModificationException).
165 */
166 private transient int modCount = 0;
167
168 /** use serialVersionUID from JDK 1.0.2 for interoperability */
169 private static final long serialVersionUID = 1421746759512286392L;
170
171 /**
172 * Constructs a new, empty hashtable with the specified initial
173 * capacity and the specified load factor.
174 *
175 * @param initialCapacity the initial capacity of the hashtable.
176 * @param loadFactor the load factor of the hashtable.
177 * @exception IllegalArgumentException if the initial capacity is less
178 * than zero, or if the load factor is nonpositive.
179 */
180 public Hashtable(int initialCapacity, float loadFactor) {
181 if (initialCapacity < 0)
182 throw new IllegalArgumentException("Illegal Capacity: "+
183 initialCapacity);
184 if (loadFactor <= 0 || Float.isNaN(loadFactor))
185 throw new IllegalArgumentException("Illegal Load: "+loadFactor);
186
187 if (initialCapacity==0)
188 initialCapacity = 1;
189 this.loadFactor = loadFactor;
190 table = new HashtableEntry<?,?>[initialCapacity];
191 // Android-changed: Ignore loadFactor when calculating threshold from initialCapacity
192 // threshold = (int)Math.min(initialCapacity * loadFactor, MAX_ARRAY_SIZE + 1);
193 threshold = (int)Math.min(initialCapacity, MAX_ARRAY_SIZE + 1);
194 }
195
196 /**
197 * Constructs a new, empty hashtable with the specified initial capacity
198 * and default load factor (0.75).
199 *
200 * @param initialCapacity the initial capacity of the hashtable.
201 * @exception IllegalArgumentException if the initial capacity is less
202 * than zero.
203 */
204 public Hashtable(int initialCapacity) {
205 this(initialCapacity, 0.75f);
206 }
207
208 /**
209 * Constructs a new, empty hashtable with a default initial capacity (11)
210 * and load factor (0.75).
211 */
212 public Hashtable() {
213 this(11, 0.75f);
214 }
215
216 /**
217 * Constructs a new hashtable with the same mappings as the given
218 * Map. The hashtable is created with an initial capacity sufficient to
219 * hold the mappings in the given Map and a default load factor (0.75).
220 *
221 * @param t the map whose mappings are to be placed in this map.
222 * @throws NullPointerException if the specified map is null.
223 * @since 1.2
224 */
225 public Hashtable(Map<? extends K, ? extends V> t) {
226 this(Math.max(2*t.size(), 11), 0.75f);
227 putAll(t);
228 }
229
230 /**
231 * Returns the number of keys in this hashtable.
232 *
233 * @return the number of keys in this hashtable.
234 */
235 public synchronized int size() {
236 return count;
237 }
238
239 /**
240 * Tests if this hashtable maps no keys to values.
241 *
242 * @return <code>true</code> if this hashtable maps no keys to values;
243 * <code>false</code> otherwise.
244 */
245 public synchronized boolean isEmpty() {
246 return count == 0;
247 }
248
249 /**
250 * Returns an enumeration of the keys in this hashtable.
251 *
252 * @return an enumeration of the keys in this hashtable.
253 * @see Enumeration
254 * @see #elements()
255 * @see #keySet()
256 * @see Map
257 */
258 public synchronized Enumeration<K> keys() {
259 return this.<K>getEnumeration(KEYS);
260 }
261
262 /**
263 * Returns an enumeration of the values in this hashtable.
264 * Use the Enumeration methods on the returned object to fetch the elements
265 * sequentially.
266 *
267 * @return an enumeration of the values in this hashtable.
268 * @see java.util.Enumeration
269 * @see #keys()
270 * @see #values()
271 * @see Map
272 */
273 public synchronized Enumeration<V> elements() {
274 return this.<V>getEnumeration(VALUES);
275 }
276
277 /**
278 * Tests if some key maps into the specified value in this hashtable.
279 * This operation is more expensive than the {@link #containsKey
280 * containsKey} method.
281 *
282 * <p>Note that this method is identical in functionality to
283 * {@link #containsValue containsValue}, (which is part of the
284 * {@link Map} interface in the collections framework).
285 *
286 * @param value a value to search for
287 * @return <code>true</code> if and only if some key maps to the
288 * <code>value</code> argument in this hashtable as
289 * determined by the <tt>equals</tt> method;
290 * <code>false</code> otherwise.
291 * @exception NullPointerException if the value is <code>null</code>
292 */
293 public synchronized boolean contains(Object value) {
294 if (value == null) {
295 throw new NullPointerException();
296 }
297
298 HashtableEntry<?,?> tab[] = table;
299 for (int i = tab.length ; i-- > 0 ;) {
300 for (HashtableEntry<?,?> e = tab[i] ; e != null ; e = e.next) {
301 if (e.value.equals(value)) {
302 return true;
303 }
304 }
305 }
306 return false;
307 }
308
309 /**
310 * Returns true if this hashtable maps one or more keys to this value.
311 *
312 * <p>Note that this method is identical in functionality to {@link
313 * #contains contains} (which predates the {@link Map} interface).
314 *
315 * @param value value whose presence in this hashtable is to be tested
316 * @return <tt>true</tt> if this map maps one or more keys to the
317 * specified value
318 * @throws NullPointerException if the value is <code>null</code>
319 * @since 1.2
320 */
321 public boolean containsValue(Object value) {
322 return contains(value);
323 }
324
325 /**
326 * Tests if the specified object is a key in this hashtable.
327 *
328 * @param key possible key
329 * @return <code>true</code> if and only if the specified object
330 * is a key in this hashtable, as determined by the
331 * <tt>equals</tt> method; <code>false</code> otherwise.
332 * @throws NullPointerException if the key is <code>null</code>
333 * @see #contains(Object)
334 */
335 public synchronized boolean containsKey(Object key) {
336 HashtableEntry<?,?> tab[] = table;
337 int hash = key.hashCode();
338 int index = (hash & 0x7FFFFFFF) % tab.length;
339 for (HashtableEntry<?,?> e = tab[index] ; e != null ; e = e.next) {
340 if ((e.hash == hash) && e.key.equals(key)) {
341 return true;
342 }
343 }
344 return false;
345 }
346
347 /**
348 * Returns the value to which the specified key is mapped,
349 * or {@code null} if this map contains no mapping for the key.
350 *
351 * <p>More formally, if this map contains a mapping from a key
352 * {@code k} to a value {@code v} such that {@code (key.equals(k))},
353 * then this method returns {@code v}; otherwise it returns
354 * {@code null}. (There can be at most one such mapping.)
355 *
356 * @param key the key whose associated value is to be returned
357 * @return the value to which the specified key is mapped, or
358 * {@code null} if this map contains no mapping for the key
359 * @throws NullPointerException if the specified key is null
360 * @see #put(Object, Object)
361 */
362 @SuppressWarnings("unchecked")
363 public synchronized V get(Object key) {
364 HashtableEntry<?,?> tab[] = table;
365 int hash = key.hashCode();
366 int index = (hash & 0x7FFFFFFF) % tab.length;
367 for (HashtableEntry<?,?> e = tab[index] ; e != null ; e = e.next) {
368 if ((e.hash == hash) && e.key.equals(key)) {
369 return (V)e.value;
370 }
371 }
372 return null;
373 }
374
375 /**
376 * The maximum size of array to allocate.
377 * Some VMs reserve some header words in an array.
378 * Attempts to allocate larger arrays may result in
379 * OutOfMemoryError: Requested array size exceeds VM limit
380 */
381 private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
382
383 /**
384 * Increases the capacity of and internally reorganizes this
385 * hashtable, in order to accommodate and access its entries more
386 * efficiently. This method is called automatically when the
387 * number of keys in the hashtable exceeds this hashtable's capacity
388 * and load factor.
389 */
390 @SuppressWarnings("unchecked")
391 protected void rehash() {
392 int oldCapacity = table.length;
393 HashtableEntry<?,?>[] oldMap = table;
394
395 // overflow-conscious code
396 int newCapacity = (oldCapacity << 1) + 1;
397 if (newCapacity - MAX_ARRAY_SIZE > 0) {
398 if (oldCapacity == MAX_ARRAY_SIZE)
399 // Keep running with MAX_ARRAY_SIZE buckets
400 return;
401 newCapacity = MAX_ARRAY_SIZE;
402 }
403 HashtableEntry<?,?>[] newMap = new HashtableEntry<?,?>[newCapacity];
404
405 modCount++;
406 threshold = (int)Math.min(newCapacity * loadFactor, MAX_ARRAY_SIZE + 1);
407 table = newMap;
408
409 for (int i = oldCapacity ; i-- > 0 ;) {
410 for (HashtableEntry<K,V> old = (HashtableEntry<K,V>)oldMap[i] ; old != null ; ) {
411 HashtableEntry<K,V> e = old;
412 old = old.next;
413
414 int index = (e.hash & 0x7FFFFFFF) % newCapacity;
415 e.next = (HashtableEntry<K,V>)newMap[index];
416 newMap[index] = e;
417 }
418 }
419 }
420
421 private void addEntry(int hash, K key, V value, int index) {
422 modCount++;
423
424 HashtableEntry<?,?> tab[] = table;
425 if (count >= threshold) {
426 // Rehash the table if the threshold is exceeded
427 rehash();
428
429 tab = table;
430 hash = key.hashCode();
431 index = (hash & 0x7FFFFFFF) % tab.length;
432 }
433
434 // Creates the new entry.
435 @SuppressWarnings("unchecked")
436 HashtableEntry<K,V> e = (HashtableEntry<K,V>) tab[index];
437 tab[index] = new HashtableEntry<>(hash, key, value, e);
438 count++;
439 }
440
441 /**
442 * Maps the specified <code>key</code> to the specified
443 * <code>value</code> in this hashtable. Neither the key nor the
444 * value can be <code>null</code>. <p>
445 *
446 * The value can be retrieved by calling the <code>get</code> method
447 * with a key that is equal to the original key.
448 *
449 * @param key the hashtable key
450 * @param value the value
451 * @return the previous value of the specified key in this hashtable,
452 * or <code>null</code> if it did not have one
453 * @exception NullPointerException if the key or value is
454 * <code>null</code>
455 * @see Object#equals(Object)
456 * @see #get(Object)
457 */
458 public synchronized V put(K key, V value) {
459 // Make sure the value is not null
460 if (value == null) {
461 throw new NullPointerException();
462 }
463
464 // Makes sure the key is not already in the hashtable.
465 HashtableEntry<?,?> tab[] = table;
466 int hash = key.hashCode();
467 int index = (hash & 0x7FFFFFFF) % tab.length;
468 @SuppressWarnings("unchecked")
469 HashtableEntry<K,V> entry = (HashtableEntry<K,V>)tab[index];
470 for(; entry != null ; entry = entry.next) {
471 if ((entry.hash == hash) && entry.key.equals(key)) {
472 V old = entry.value;
473 entry.value = value;
474 return old;
475 }
476 }
477
478 addEntry(hash, key, value, index);
479 return null;
480 }
481
482 /**
483 * Removes the key (and its corresponding value) from this
484 * hashtable. This method does nothing if the key is not in the hashtable.
485 *
486 * @param key the key that needs to be removed
487 * @return the value to which the key had been mapped in this hashtable,
488 * or <code>null</code> if the key did not have a mapping
489 * @throws NullPointerException if the key is <code>null</code>
490 */
491 public synchronized V remove(Object key) {
492 HashtableEntry<?,?> tab[] = table;
493 int hash = key.hashCode();
494 int index = (hash & 0x7FFFFFFF) % tab.length;
495 @SuppressWarnings("unchecked")
496 HashtableEntry<K,V> e = (HashtableEntry<K,V>)tab[index];
497 for(HashtableEntry<K,V> prev = null ; e != null ; prev = e, e = e.next) {
498 if ((e.hash == hash) && e.key.equals(key)) {
499 modCount++;
500 if (prev != null) {
501 prev.next = e.next;
502 } else {
503 tab[index] = e.next;
504 }
505 count--;
506 V oldValue = e.value;
507 e.value = null;
508 return oldValue;
509 }
510 }
511 return null;
512 }
513
514 /**
515 * Copies all of the mappings from the specified map to this hashtable.
516 * These mappings will replace any mappings that this hashtable had for any
517 * of the keys currently in the specified map.
518 *
519 * @param t mappings to be stored in this map
520 * @throws NullPointerException if the specified map is null
521 * @since 1.2
522 */
523 public synchronized void putAll(Map<? extends K, ? extends V> t) {
524 for (Map.Entry<? extends K, ? extends V> e : t.entrySet())
525 put(e.getKey(), e.getValue());
526 }
527
528 /**
529 * Clears this hashtable so that it contains no keys.
530 */
531 public synchronized void clear() {
532 HashtableEntry<?,?> tab[] = table;
533 modCount++;
534 for (int index = tab.length; --index >= 0; )
535 tab[index] = null;
536 count = 0;
537 }
538
539 /**
540 * Creates a shallow copy of this hashtable. All the structure of the
541 * hashtable itself is copied, but the keys and values are not cloned.
542 * This is a relatively expensive operation.
543 *
544 * @return a clone of the hashtable
545 */
546 public synchronized Object clone() {
547 try {
548 Hashtable<?,?> t = (Hashtable<?,?>)super.clone();
549 t.table = new HashtableEntry<?,?>[table.length];
550 for (int i = table.length ; i-- > 0 ; ) {
551 t.table[i] = (table[i] != null)
552 ? (HashtableEntry<?,?>) table[i].clone() : null;
553 }
554 t.keySet = null;
555 t.entrySet = null;
556 t.values = null;
557 t.modCount = 0;
558 return t;
559 } catch (CloneNotSupportedException e) {
560 // this shouldn't happen, since we are Cloneable
561 throw new InternalError(e);
562 }
563 }
564
565 /**
566 * Returns a string representation of this <tt>Hashtable</tt> object
567 * in the form of a set of entries, enclosed in braces and separated
568 * by the ASCII characters "<tt>,&nbsp;</tt>" (comma and space). Each
569 * entry is rendered as the key, an equals sign <tt>=</tt>, and the
570 * associated element, where the <tt>toString</tt> method is used to
571 * convert the key and element to strings.
572 *
573 * @return a string representation of this hashtable
574 */
575 public synchronized String toString() {
576 int max = size() - 1;
577 if (max == -1)
578 return "{}";
579
580 StringBuilder sb = new StringBuilder();
581 Iterator<Map.Entry<K,V>> it = entrySet().iterator();
582
583 sb.append('{');
584 for (int i = 0; ; i++) {
585 Map.Entry<K,V> e = it.next();
586 K key = e.getKey();
587 V value = e.getValue();
588 sb.append(key == this ? "(this Map)" : key.toString());
589 sb.append('=');
590 sb.append(value == this ? "(this Map)" : value.toString());
591
592 if (i == max)
593 return sb.append('}').toString();
594 sb.append(", ");
595 }
596 }
597
598
599 private <T> Enumeration<T> getEnumeration(int type) {
600 if (count == 0) {
601 return Collections.emptyEnumeration();
602 } else {
603 return new Enumerator<>(type, false);
604 }
605 }
606
607 private <T> Iterator<T> getIterator(int type) {
608 if (count == 0) {
609 return Collections.emptyIterator();
610 } else {
611 return new Enumerator<>(type, true);
612 }
613 }
614
615 // Views
616
617 /**
618 * Each of these fields are initialized to contain an instance of the
619 * appropriate view the first time this view is requested. The views are
620 * stateless, so there's no reason to create more than one of each.
621 */
622 private transient volatile Set<K> keySet;
623 private transient volatile Set<Map.Entry<K,V>> entrySet;
624 private transient volatile Collection<V> values;
625
626 /**
627 * Returns a {@link Set} view of the keys contained in this map.
628 * The set is backed by the map, so changes to the map are
629 * reflected in the set, and vice-versa. If the map is modified
630 * while an iteration over the set is in progress (except through
631 * the iterator's own <tt>remove</tt> operation), the results of
632 * the iteration are undefined. The set supports element removal,
633 * which removes the corresponding mapping from the map, via the
634 * <tt>Iterator.remove</tt>, <tt>Set.remove</tt>,
635 * <tt>removeAll</tt>, <tt>retainAll</tt>, and <tt>clear</tt>
636 * operations. It does not support the <tt>add</tt> or <tt>addAll</tt>
637 * operations.
638 *
639 * @since 1.2
640 */
641 public Set<K> keySet() {
642 if (keySet == null)
643 keySet = Collections.synchronizedSet(new KeySet(), this);
644 return keySet;
645 }
646
647 private class KeySet extends AbstractSet<K> {
648 public Iterator<K> iterator() {
649 return getIterator(KEYS);
650 }
651 public int size() {
652 return count;
653 }
654 public boolean contains(Object o) {
655 return containsKey(o);
656 }
657 public boolean remove(Object o) {
658 return Hashtable.this.remove(o) != null;
659 }
660 public void clear() {
661 Hashtable.this.clear();
662 }
663 }
664
665 /**
666 * Returns a {@link Set} view of the mappings contained in this map.
667 * The set is backed by the map, so changes to the map are
668 * reflected in the set, and vice-versa. If the map is modified
669 * while an iteration over the set is in progress (except through
670 * the iterator's own <tt>remove</tt> operation, or through the
671 * <tt>setValue</tt> operation on a map entry returned by the
672 * iterator) the results of the iteration are undefined. The set
673 * supports element removal, which removes the corresponding
674 * mapping from the map, via the <tt>Iterator.remove</tt>,
675 * <tt>Set.remove</tt>, <tt>removeAll</tt>, <tt>retainAll</tt> and
676 * <tt>clear</tt> operations. It does not support the
677 * <tt>add</tt> or <tt>addAll</tt> operations.
678 *
679 * @since 1.2
680 */
681 public Set<Map.Entry<K,V>> entrySet() {
682 if (entrySet==null)
683 entrySet = Collections.synchronizedSet(new EntrySet(), this);
684 return entrySet;
685 }
686
687 private class EntrySet extends AbstractSet<Map.Entry<K,V>> {
688 public Iterator<Map.Entry<K,V>> iterator() {
689 return getIterator(ENTRIES);
690 }
691
692 public boolean add(Map.Entry<K,V> o) {
693 return super.add(o);
694 }
695
696 public boolean contains(Object o) {
697 if (!(o instanceof Map.Entry))
698 return false;
699 Map.Entry<?,?> entry = (Map.Entry<?,?>)o;
700 Object key = entry.getKey();
701 HashtableEntry<?,?>[] tab = table;
702 int hash = key.hashCode();
703 int index = (hash & 0x7FFFFFFF) % tab.length;
704
705 for (HashtableEntry<?,?> e = tab[index]; e != null; e = e.next)
706 if (e.hash==hash && e.equals(entry))
707 return true;
708 return false;
709 }
710
711 public boolean remove(Object o) {
712 if (!(o instanceof Map.Entry))
713 return false;
714 Map.Entry<?,?> entry = (Map.Entry<?,?>) o;
715 Object key = entry.getKey();
716 HashtableEntry<?,?>[] tab = table;
717 int hash = key.hashCode();
718 int index = (hash & 0x7FFFFFFF) % tab.length;
719
720 @SuppressWarnings("unchecked")
721 HashtableEntry<K,V> e = (HashtableEntry<K,V>)tab[index];
722 for(HashtableEntry<K,V> prev = null; e != null; prev = e, e = e.next) {
723 if (e.hash==hash && e.equals(entry)) {
724 modCount++;
725 if (prev != null)
726 prev.next = e.next;
727 else
728 tab[index] = e.next;
729
730 count--;
731 e.value = null;
732 return true;
733 }
734 }
735 return false;
736 }
737
738 public int size() {
739 return count;
740 }
741
742 public void clear() {
743 Hashtable.this.clear();
744 }
745 }
746
747 /**
748 * Returns a {@link Collection} view of the values contained in this map.
749 * The collection is backed by the map, so changes to the map are
750 * reflected in the collection, and vice-versa. If the map is
751 * modified while an iteration over the collection is in progress
752 * (except through the iterator's own <tt>remove</tt> operation),
753 * the results of the iteration are undefined. The collection
754 * supports element removal, which removes the corresponding
755 * mapping from the map, via the <tt>Iterator.remove</tt>,
756 * <tt>Collection.remove</tt>, <tt>removeAll</tt>,
757 * <tt>retainAll</tt> and <tt>clear</tt> operations. It does not
758 * support the <tt>add</tt> or <tt>addAll</tt> operations.
759 *
760 * @since 1.2
761 */
762 public Collection<V> values() {
763 if (values==null)
764 values = Collections.synchronizedCollection(new ValueCollection(),
765 this);
766 return values;
767 }
768
769 private class ValueCollection extends AbstractCollection<V> {
770 public Iterator<V> iterator() {
771 return getIterator(VALUES);
772 }
773 public int size() {
774 return count;
775 }
776 public boolean contains(Object o) {
777 return containsValue(o);
778 }
779 public void clear() {
780 Hashtable.this.clear();
781 }
782 }
783
784 // Comparison and hashing
785
786 /**
787 * Compares the specified Object with this Map for equality,
788 * as per the definition in the Map interface.
789 *
790 * @param o object to be compared for equality with this hashtable
791 * @return true if the specified Object is equal to this Map
792 * @see Map#equals(Object)
793 * @since 1.2
794 */
795 public synchronized boolean equals(Object o) {
796 if (o == this)
797 return true;
798
799 if (!(o instanceof Map))
800 return false;
801 Map<?,?> t = (Map<?,?>) o;
802 if (t.size() != size())
803 return false;
804
805 try {
806 Iterator<Map.Entry<K,V>> i = entrySet().iterator();
807 while (i.hasNext()) {
808 Map.Entry<K,V> e = i.next();
809 K key = e.getKey();
810 V value = e.getValue();
811 if (value == null) {
812 if (!(t.get(key)==null && t.containsKey(key)))
813 return false;
814 } else {
815 if (!value.equals(t.get(key)))
816 return false;
817 }
818 }
819 } catch (ClassCastException unused) {
820 return false;
821 } catch (NullPointerException unused) {
822 return false;
823 }
824
825 return true;
826 }
827
828 /**
829 * Returns the hash code value for this Map as per the definition in the
830 * Map interface.
831 *
832 * @see Map#hashCode()
833 * @since 1.2
834 */
835 public synchronized int hashCode() {
836 /*
837 * This code detects the recursion caused by computing the hash code
838 * of a self-referential hash table and prevents the stack overflow
839 * that would otherwise result. This allows certain 1.1-era
840 * applets with self-referential hash tables to work. This code
841 * abuses the loadFactor field to do double-duty as a hashCode
842 * in progress flag, so as not to worsen the space performance.
843 * A negative load factor indicates that hash code computation is
844 * in progress.
845 */
846 int h = 0;
847 if (count == 0 || loadFactor < 0)
848 return h; // Returns zero
849
850 loadFactor = -loadFactor; // Mark hashCode computation in progress
851 HashtableEntry<?,?>[] tab = table;
852 for (HashtableEntry<?,?> entry : tab) {
853 while (entry != null) {
854 h += entry.hashCode();
855 entry = entry.next;
856 }
857 }
858
859 loadFactor = -loadFactor; // Mark hashCode computation complete
860
861 return h;
862 }
863
864 @Override
865 public synchronized V getOrDefault(Object key, V defaultValue) {
866 V result = get(key);
867 return (null == result) ? defaultValue : result;
868 }
869
870 @SuppressWarnings("unchecked")
871 @Override
872 public synchronized void forEach(BiConsumer<? super K, ? super V> action) {
873 Objects.requireNonNull(action); // explicit check required in case
874 // table is empty.
875 final int expectedModCount = modCount;
876
877 HashtableEntry<?, ?>[] tab = table;
878 for (HashtableEntry<?, ?> entry : tab) {
879 while (entry != null) {
880 action.accept((K)entry.key, (V)entry.value);
881 entry = entry.next;
882
883 if (expectedModCount != modCount) {
884 throw new ConcurrentModificationException();
885 }
886 }
887 }
888 }
889
890 @SuppressWarnings("unchecked")
891 @Override
892 public synchronized void replaceAll(BiFunction<? super K, ? super V, ? extends V> function) {
893 Objects.requireNonNull(function); // explicit check required in case
894 // table is empty.
895 final int expectedModCount = modCount;
896
897 HashtableEntry<K, V>[] tab = (HashtableEntry<K, V>[])table;
898 for (HashtableEntry<K, V> entry : tab) {
899 while (entry != null) {
900 entry.value = Objects.requireNonNull(
901 function.apply(entry.key, entry.value));
902 entry = entry.next;
903
904 if (expectedModCount != modCount) {
905 throw new ConcurrentModificationException();
906 }
907 }
908 }
909 }
910
911 @Override
912 public synchronized V putIfAbsent(K key, V value) {
913 Objects.requireNonNull(value);
914
915 // Makes sure the key is not already in the hashtable.
916 HashtableEntry<?,?> tab[] = table;
917 int hash = key.hashCode();
918 int index = (hash & 0x7FFFFFFF) % tab.length;
919 @SuppressWarnings("unchecked")
920 HashtableEntry<K,V> entry = (HashtableEntry<K,V>)tab[index];
921 for (; entry != null; entry = entry.next) {
922 if ((entry.hash == hash) && entry.key.equals(key)) {
923 V old = entry.value;
924 if (old == null) {
925 entry.value = value;
926 }
927 return old;
928 }
929 }
930
931 addEntry(hash, key, value, index);
932 return null;
933 }
934
935 @Override
936 public synchronized boolean remove(Object key, Object value) {
937 Objects.requireNonNull(value);
938
939 HashtableEntry<?,?> tab[] = table;
940 int hash = key.hashCode();
941 int index = (hash & 0x7FFFFFFF) % tab.length;
942 @SuppressWarnings("unchecked")
943 HashtableEntry<K,V> e = (HashtableEntry<K,V>)tab[index];
944 for (HashtableEntry<K,V> prev = null; e != null; prev = e, e = e.next) {
945 if ((e.hash == hash) && e.key.equals(key) && e.value.equals(value)) {
946 modCount++;
947 if (prev != null) {
948 prev.next = e.next;
949 } else {
950 tab[index] = e.next;
951 }
952 count--;
953 e.value = null;
954 return true;
955 }
956 }
957 return false;
958 }
959
960 @Override
961 public synchronized boolean replace(K key, V oldValue, V newValue) {
962 Objects.requireNonNull(oldValue);
963 Objects.requireNonNull(newValue);
964 HashtableEntry<?,?> tab[] = table;
965 int hash = key.hashCode();
966 int index = (hash & 0x7FFFFFFF) % tab.length;
967 @SuppressWarnings("unchecked")
968 HashtableEntry<K,V> e = (HashtableEntry<K,V>)tab[index];
969 for (; e != null; e = e.next) {
970 if ((e.hash == hash) && e.key.equals(key)) {
971 if (e.value.equals(oldValue)) {
972 e.value = newValue;
973 return true;
974 } else {
975 return false;
976 }
977 }
978 }
979 return false;
980 }
981
982 @Override
983 public synchronized V replace(K key, V value) {
984 Objects.requireNonNull(value);
985 HashtableEntry<?,?> tab[] = table;
986 int hash = key.hashCode();
987 int index = (hash & 0x7FFFFFFF) % tab.length;
988 @SuppressWarnings("unchecked")
989 HashtableEntry<K,V> e = (HashtableEntry<K,V>)tab[index];
990 for (; e != null; e = e.next) {
991 if ((e.hash == hash) && e.key.equals(key)) {
992 V oldValue = e.value;
993 e.value = value;
994 return oldValue;
995 }
996 }
997 return null;
998 }
999
1000 @Override
1001 public synchronized V computeIfAbsent(K key, Function<? super K, ? extends V> mappingFunction) {
1002 Objects.requireNonNull(mappingFunction);
1003
1004 HashtableEntry<?,?> tab[] = table;
1005 int hash = key.hashCode();
1006 int index = (hash & 0x7FFFFFFF) % tab.length;
1007 @SuppressWarnings("unchecked")
1008 HashtableEntry<K,V> e = (HashtableEntry<K,V>)tab[index];
1009 for (; e != null; e = e.next) {
1010 if (e.hash == hash && e.key.equals(key)) {
1011 // Hashtable not accept null value
1012 return e.value;
1013 }
1014 }
1015
1016 V newValue = mappingFunction.apply(key);
1017 if (newValue != null) {
1018 addEntry(hash, key, newValue, index);
1019 }
1020
1021 return newValue;
1022 }
1023
1024 @Override
1025 public synchronized V computeIfPresent(K key, BiFunction<? super K, ? super V, ? extends V> remappingFunction) {
1026 Objects.requireNonNull(remappingFunction);
1027
1028 HashtableEntry<?,?> tab[] = table;
1029 int hash = key.hashCode();
1030 int index = (hash & 0x7FFFFFFF) % tab.length;
1031 @SuppressWarnings("unchecked")
1032 HashtableEntry<K,V> e = (HashtableEntry<K,V>)tab[index];
1033 for (HashtableEntry<K,V> prev = null; e != null; prev = e, e = e.next) {
1034 if (e.hash == hash && e.key.equals(key)) {
1035 V newValue = remappingFunction.apply(key, e.value);
1036 if (newValue == null) {
1037 modCount++;
1038 if (prev != null) {
1039 prev.next = e.next;
1040 } else {
1041 tab[index] = e.next;
1042 }
1043 count--;
1044 } else {
1045 e.value = newValue;
1046 }
1047 return newValue;
1048 }
1049 }
1050 return null;
1051 }
1052
1053 @Override
1054 public synchronized V compute(K key, BiFunction<? super K, ? super V, ? extends V> remappingFunction) {
1055 Objects.requireNonNull(remappingFunction);
1056
1057 HashtableEntry<?,?> tab[] = table;
1058 int hash = key.hashCode();
1059 int index = (hash & 0x7FFFFFFF) % tab.length;
1060 @SuppressWarnings("unchecked")
1061 HashtableEntry<K,V> e = (HashtableEntry<K,V>)tab[index];
1062 for (HashtableEntry<K,V> prev = null; e != null; prev = e, e = e.next) {
1063 if (e.hash == hash && Objects.equals(e.key, key)) {
1064 V newValue = remappingFunction.apply(key, e.value);
1065 if (newValue == null) {
1066 modCount++;
1067 if (prev != null) {
1068 prev.next = e.next;
1069 } else {
1070 tab[index] = e.next;
1071 }
1072 count--;
1073 } else {
1074 e.value = newValue;
1075 }
1076 return newValue;
1077 }
1078 }
1079
1080 V newValue = remappingFunction.apply(key, null);
1081 if (newValue != null) {
1082 addEntry(hash, key, newValue, index);
1083 }
1084
1085 return newValue;
1086 }
1087
1088 @Override
1089 public synchronized V merge(K key, V value, BiFunction<? super V, ? super V, ? extends V> remappingFunction) {
1090 Objects.requireNonNull(remappingFunction);
1091
1092 HashtableEntry<?,?> tab[] = table;
1093 int hash = key.hashCode();
1094 int index = (hash & 0x7FFFFFFF) % tab.length;
1095 @SuppressWarnings("unchecked")
1096 HashtableEntry<K,V> e = (HashtableEntry<K,V>)tab[index];
1097 for (HashtableEntry<K,V> prev = null; e != null; prev = e, e = e.next) {
1098 if (e.hash == hash && e.key.equals(key)) {
1099 V newValue = remappingFunction.apply(e.value, value);
1100 if (newValue == null) {
1101 modCount++;
1102 if (prev != null) {
1103 prev.next = e.next;
1104 } else {
1105 tab[index] = e.next;
1106 }
1107 count--;
1108 } else {
1109 e.value = newValue;
1110 }
1111 return newValue;
1112 }
1113 }
1114
1115 if (value != null) {
1116 addEntry(hash, key, value, index);
1117 }
1118
1119 return value;
1120 }
1121
1122 /**
1123 * Save the state of the Hashtable to a stream (i.e., serialize it).
1124 *
1125 * @serialData The <i>capacity</i> of the Hashtable (the length of the
1126 * bucket array) is emitted (int), followed by the
1127 * <i>size</i> of the Hashtable (the number of key-value
1128 * mappings), followed by the key (Object) and value (Object)
1129 * for each key-value mapping represented by the Hashtable
1130 * The key-value mappings are emitted in no particular order.
1131 */
1132 private void writeObject(java.io.ObjectOutputStream s)
1133 throws IOException {
1134 HashtableEntry<Object, Object> entryStack = null;
1135
1136 synchronized (this) {
1137 // Write out the threshold and loadFactor
1138 s.defaultWriteObject();
1139
1140 // Write out the length and count of elements
1141 s.writeInt(table.length);
1142 s.writeInt(count);
1143
1144 // Stack copies of the entries in the table
1145 for (int index = 0; index < table.length; index++) {
1146 HashtableEntry<?,?> entry = table[index];
1147
1148 while (entry != null) {
1149 entryStack =
1150 new HashtableEntry<>(0, entry.key, entry.value, entryStack);
1151 entry = entry.next;
1152 }
1153 }
1154 }
1155
1156 // Write out the key/value objects from the stacked entries
1157 while (entryStack != null) {
1158 s.writeObject(entryStack.key);
1159 s.writeObject(entryStack.value);
1160 entryStack = entryStack.next;
1161 }
1162 }
1163
1164 /**
1165 * Reconstitute the Hashtable from a stream (i.e., deserialize it).
1166 */
1167 private void readObject(java.io.ObjectInputStream s)
1168 throws IOException, ClassNotFoundException
1169 {
1170 // Read in the threshold and loadFactor
1171 s.defaultReadObject();
1172
1173 // Validate loadFactor (ignore threshold - it will be re-computed)
1174 if (loadFactor <= 0 || Float.isNaN(loadFactor))
1175 throw new StreamCorruptedException("Illegal Load: " + loadFactor);
1176
1177 // Read the original length of the array and number of elements
1178 int origlength = s.readInt();
1179 int elements = s.readInt();
1180
1181 // Validate # of elements
1182 if (elements < 0)
1183 throw new StreamCorruptedException("Illegal # of Elements: " + elements);
1184
1185 // Clamp original length to be more than elements / loadFactor
1186 // (this is the invariant enforced with auto-growth)
1187 origlength = Math.max(origlength, (int)(elements / loadFactor) + 1);
1188
1189 // Compute new length with a bit of room 5% + 3 to grow but
1190 // no larger than the clamped original length. Make the length
1191 // odd if it's large enough, this helps distribute the entries.
1192 // Guard against the length ending up zero, that's not valid.
1193 int length = (int)((elements + elements / 20) / loadFactor) + 3;
1194 if (length > elements && (length & 1) == 0)
1195 length--;
1196 length = Math.min(length, origlength);
1197 table = new HashtableEntry<?,?>[length];
1198 threshold = (int)Math.min(length * loadFactor, MAX_ARRAY_SIZE + 1);
1199 count = 0;
1200
1201 // Read the number of elements and then all the key/value objects
1202 for (; elements > 0; elements--) {
1203 @SuppressWarnings("unchecked")
1204 K key = (K)s.readObject();
1205 @SuppressWarnings("unchecked")
1206 V value = (V)s.readObject();
1207 // sync is eliminated for performance
1208 reconstitutionPut(table, key, value);
1209 }
1210 }
1211
1212 /**
1213 * The put method used by readObject. This is provided because put
1214 * is overridable and should not be called in readObject since the
1215 * subclass will not yet be initialized.
1216 *
1217 * <p>This differs from the regular put method in several ways. No
1218 * checking for rehashing is necessary since the number of elements
1219 * initially in the table is known. The modCount is not incremented and
1220 * there's no synchronization because we are creating a new instance.
1221 * Also, no return value is needed.
1222 */
1223 private void reconstitutionPut(HashtableEntry<?,?>[] tab, K key, V value)
1224 throws StreamCorruptedException
1225 {
1226 if (value == null) {
1227 throw new java.io.StreamCorruptedException();
1228 }
1229 // Makes sure the key is not already in the hashtable.
1230 // This should not happen in deserialized version.
1231 int hash = key.hashCode();
1232 int index = (hash & 0x7FFFFFFF) % tab.length;
1233 for (HashtableEntry<?,?> e = tab[index] ; e != null ; e = e.next) {
1234 if ((e.hash == hash) && e.key.equals(key)) {
1235 throw new java.io.StreamCorruptedException();
1236 }
1237 }
1238 // Creates the new entry.
1239 @SuppressWarnings("unchecked")
1240 HashtableEntry<K,V> e = (HashtableEntry<K,V>)tab[index];
1241 tab[index] = new HashtableEntry<>(hash, key, value, e);
1242 count++;
1243 }
1244
1245 /**
1246 * Hashtable bucket collision list entry
1247 */
1248 // BEGIN Android-changed: Renamed Entry -> HashtableEntry.
1249 // Code references to "HashTable.Entry" must mean Map.Entry
1250 //
1251 // This mirrors the corresponding rename of LinkedHashMap's
1252 // Entry->LinkedHashMapEntry.
1253 //
1254 // This is for source compatibility with earlier versions of Android.
1255 // Otherwise, it would hide Map.Entry which would break compilation
1256 // of code like:
1257 //
1258 // Hashtable.Entry<K, V> entry = hashtable.entrySet().iterator.next();
1259 //
1260 // To compile, that code snippet's "HashtableMap.Entry" must
1261 // mean java.util.Map.Entry which is the compile time type of
1262 // entrySet()'s elements.
1263 //
1264 private static class HashtableEntry<K,V> implements Map.Entry<K,V> {
1265 // END Android-changed: Renamed Entry -> HashtableEntry.
1266 final int hash;
1267 final K key;
1268 V value;
1269 HashtableEntry<K,V> next;
1270
1271 protected HashtableEntry(int hash, K key, V value, HashtableEntry<K,V> next) {
1272 this.hash = hash;
1273 this.key = key;
1274 this.value = value;
1275 this.next = next;
1276 }
1277
1278 @SuppressWarnings("unchecked")
1279 protected Object clone() {
1280 return new HashtableEntry<>(hash, key, value,
1281 (next==null ? null : (HashtableEntry<K,V>) next.clone()));
1282 }
1283
1284 // Map.Entry Ops
1285
1286 public K getKey() {
1287 return key;
1288 }
1289
1290 public V getValue() {
1291 return value;
1292 }
1293
1294 public V setValue(V value) {
1295 if (value == null)
1296 throw new NullPointerException();
1297
1298 V oldValue = this.value;
1299 this.value = value;
1300 return oldValue;
1301 }
1302
1303 public boolean equals(Object o) {
1304 if (!(o instanceof Map.Entry))
1305 return false;
1306 Map.Entry<?,?> e = (Map.Entry<?,?>)o;
1307
1308 return (key==null ? e.getKey()==null : key.equals(e.getKey())) &&
1309 (value==null ? e.getValue()==null : value.equals(e.getValue()));
1310 }
1311
1312 public int hashCode() {
1313 return hash ^ Objects.hashCode(value);
1314 }
1315
1316 public String toString() {
1317 return key.toString()+"="+value.toString();
1318 }
1319 }
1320
1321 // Types of Enumerations/Iterations
1322 private static final int KEYS = 0;
1323 private static final int VALUES = 1;
1324 private static final int ENTRIES = 2;
1325
1326 /**
1327 * A hashtable enumerator class. This class implements both the
1328 * Enumeration and Iterator interfaces, but individual instances
1329 * can be created with the Iterator methods disabled. This is necessary
1330 * to avoid unintentionally increasing the capabilities granted a user
1331 * by passing an Enumeration.
1332 */
1333 private class Enumerator<T> implements Enumeration<T>, Iterator<T> {
1334 HashtableEntry<?,?>[] table = Hashtable.this.table;
1335 int index = table.length;
1336 HashtableEntry<?,?> entry;
1337 HashtableEntry<?,?> lastReturned;
1338 int type;
1339
1340 /**
1341 * Indicates whether this Enumerator is serving as an Iterator
1342 * or an Enumeration. (true -> Iterator).
1343 */
1344 boolean iterator;
1345
1346 /**
1347 * The modCount value that the iterator believes that the backing
1348 * Hashtable should have. If this expectation is violated, the iterator
1349 * has detected concurrent modification.
1350 */
1351 protected int expectedModCount = modCount;
1352
1353 Enumerator(int type, boolean iterator) {
1354 this.type = type;
1355 this.iterator = iterator;
1356 }
1357
1358 public boolean hasMoreElements() {
1359 HashtableEntry<?,?> e = entry;
1360 int i = index;
1361 HashtableEntry<?,?>[] t = table;
1362 /* Use locals for faster loop iteration */
1363 while (e == null && i > 0) {
1364 e = t[--i];
1365 }
1366 entry = e;
1367 index = i;
1368 return e != null;
1369 }
1370
1371 @SuppressWarnings("unchecked")
1372 public T nextElement() {
1373 HashtableEntry<?,?> et = entry;
1374 int i = index;
1375 HashtableEntry<?,?>[] t = table;
1376 /* Use locals for faster loop iteration */
1377 while (et == null && i > 0) {
1378 et = t[--i];
1379 }
1380 entry = et;
1381 index = i;
1382 if (et != null) {
1383 HashtableEntry<?,?> e = lastReturned = entry;
1384 entry = e.next;
1385 return type == KEYS ? (T)e.key : (type == VALUES ? (T)e.value : (T)e);
1386 }
1387 throw new NoSuchElementException("Hashtable Enumerator");
1388 }
1389
1390 // Iterator methods
1391 public boolean hasNext() {
1392 return hasMoreElements();
1393 }
1394
1395 public T next() {
1396 if (modCount != expectedModCount)
1397 throw new ConcurrentModificationException();
1398 return nextElement();
1399 }
1400
1401 public void remove() {
1402 if (!iterator)
1403 throw new UnsupportedOperationException();
1404 if (lastReturned == null)
1405 throw new IllegalStateException("Hashtable Enumerator");
1406 if (modCount != expectedModCount)
1407 throw new ConcurrentModificationException();
1408
1409 synchronized(Hashtable.this) {
1410 HashtableEntry<?,?>[] tab = Hashtable.this.table;
1411 int index = (lastReturned.hash & 0x7FFFFFFF) % tab.length;
1412
1413 @SuppressWarnings("unchecked")
1414 HashtableEntry<K,V> e = (HashtableEntry<K,V>)tab[index];
1415 for(HashtableEntry<K,V> prev = null; e != null; prev = e, e = e.next) {
1416 if (e == lastReturned) {
1417 modCount++;
1418 expectedModCount++;
1419 if (prev == null)
1420 tab[index] = e.next;
1421 else
1422 prev.next = e.next;
1423 count--;
1424 lastReturned = null;
1425 return;
1426 }
1427 }
1428 throw new ConcurrentModificationException();
1429 }
1430 }
1431 }
1432}