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