src/share/classes/java/util/Hashtable.java
Print this page
rev 6858 : 8004518: Add in-place operations to Map
8010122: Add defaults for ConcurrentMap operations to Map
Reviewed-by: darcy, briangoetz, mduigou, dholmes, ulfzibis
Contributed-by: Doug Lea <dl at cs.oswego.edu>, Henry Jen <henry.jen@oracle.com>, Akhil Arora <akhil.arora@oracle.com>, Peter Levart <peter.levart@gmail.com>
@@ -1,7 +1,7 @@
/*
- * Copyright (c) 1994, 2012, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1994, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
@@ -22,11 +22,16 @@
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package java.util;
+
import java.io.*;
+import java.util.function.BiConsumer;
+import java.util.function.Function;
+import java.util.function.BiFunction;
+
/**
* This class implements a hash table, which maps keys to values. Any
* non-<code>null</code> object can be used as a key or as a value. <p>
*
@@ -453,10 +458,30 @@
newMap[index] = e;
}
}
}
+ private void addEntry(int hash, K key, V value, int index) {
+ modCount++;
+
+ Entry<?,?> tab[] = table;
+ if (count >= threshold) {
+ // Rehash the table if the threshold is exceeded
+ rehash();
+
+ tab = table;
+ hash = hash(key);
+ index = (hash & 0x7FFFFFFF) % tab.length;
+ }
+
+ // Creates the new entry.
+ @SuppressWarnings("unchecked")
+ Entry<K,V> e = (Entry<K,V>) tab[index];
+ tab[index] = new Entry<>(hash, key, value, e);
+ count++;
+ }
+
/**
* Maps the specified <code>key</code> to the specified
* <code>value</code> in this hashtable. Neither the key nor the
* value can be <code>null</code>. <p>
*
@@ -490,25 +515,11 @@
entry.value = value;
return old;
}
}
- modCount++;
- if (count >= threshold) {
- // Rehash the table if the threshold is exceeded
- rehash();
-
- tab = table;
- hash = hash(key);
- index = (hash & 0x7FFFFFFF) % tab.length;
- }
-
- // Creates the new entry.
- @SuppressWarnings("unchecked")
- Entry<K,V> e = (Entry<K,V>)tab[index];
- tab[index] = new Entry<>(hash, key, value, e);
- count++;
+ addEntry(hash, key, value, index);
return null;
}
/**
* Removes the key (and its corresponding value) from this
@@ -890,10 +901,241 @@
loadFactor = -loadFactor; // Mark hashCode computation complete
return h;
}
+ @Override
+ public synchronized V getOrDefault(Object key, V defaultValue) {
+ V result = get(key);
+ return (null == result) ? defaultValue : result;
+ }
+
+ @Override
+ public synchronized void forEach(BiConsumer<? super K, ? super V> consumer) {
+ Entry<?,?>[] tab = table;
+ for (Entry<?,?> entry : tab) {
+ while (entry != null) {
+ consumer.accept( (K) entry.key, (V) entry.value);
+ entry = entry.next;
+ }
+ }
+ }
+
+ @Override
+ public synchronized void replaceAll(
+ BiFunction<? super K, ? super V, ? extends V> function) {
+ Map.super.replaceAll(function);
+ }
+
+ @Override
+ public synchronized V putIfAbsent(K key, V value) {
+ Objects.requireNonNull(value);
+
+ // Makes sure the key is not already in the hashtable.
+ Entry<?,?> tab[] = table;
+ int hash = hash(key);
+ int index = (hash & 0x7FFFFFFF) % tab.length;
+ @SuppressWarnings("unchecked")
+ Entry<K,V> entry = (Entry<K,V>)tab[index];
+ for(; entry != null ; entry = entry.next) {
+ if ((entry.hash == hash) && entry.key.equals(key)) {
+ V old = entry.value;
+ if (old == null) {
+ entry.value = value;
+ }
+ return old;
+ }
+ }
+
+ addEntry(hash, key, value, index);
+ return null;
+ }
+
+ @Override
+ public synchronized boolean remove(Object key, Object value) {
+ Objects.requireNonNull(value);
+
+ Entry<?,?> tab[] = table;
+ int hash = hash(key);
+ int index = (hash & 0x7FFFFFFF) % tab.length;
+ @SuppressWarnings("unchecked")
+ Entry<K,V> e = (Entry<K,V>)tab[index];
+ for(Entry<K,V> prev = null ; e != null ; prev = e, e = e.next) {
+ if ((e.hash == hash) && e.key.equals(key) && e.value.equals(value)) {
+ modCount++;
+ if (prev != null) {
+ prev.next = e.next;
+ } else {
+ tab[index] = e.next;
+ }
+ count--;
+ e.value = null;
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public synchronized boolean replace(K key, V oldValue, V newValue) {
+ Entry<?,?> tab[] = table;
+ int hash = hash(key);
+ int index = (hash & 0x7FFFFFFF) % tab.length;
+ @SuppressWarnings("unchecked")
+ Entry<K,V> e = (Entry<K,V>) tab[index];
+ for (; e != null ; e = e.next) {
+ if ((e.hash == hash) && e.key.equals(key)) {
+ if (e.value.equals(oldValue)) {
+ e.value = newValue;
+ return true;
+ } else {
+ return false;
+ }
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public synchronized V replace(K key, V value) {
+ Entry<?,?> tab[] = table;
+ int hash = hash(key);
+ int index = (hash & 0x7FFFFFFF) % tab.length;
+ @SuppressWarnings("unchecked")
+ Entry<K,V> e = (Entry<K,V>) tab[index];
+ for (; e != null; e = e.next) {
+ if ((e.hash == hash) && e.key.equals(key)) {
+ V oldValue = e.value;
+ e.value = value;
+ return oldValue;
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public synchronized V computeIfAbsent(K key, Function<? super K, ? extends V> mappingFunction) {
+ Objects.requireNonNull(mappingFunction);
+
+ Entry<?,?> tab[] = table;
+ int hash = hash(key);
+ int index = (hash & 0x7FFFFFFF) % tab.length;
+ @SuppressWarnings("unchecked")
+ Entry<K,V> e = (Entry<K,V>) tab[index];
+ for(; e != null; e = e.next) {
+ if (e.hash == hash && e.key.equals(key)) {
+ // Hashtable not accept null value
+ return e.value;
+ }
+ }
+
+ V newValue = mappingFunction.apply(key);
+ if (newValue != null) {
+ addEntry(hash, key, newValue, index);
+ }
+
+ return newValue;
+ }
+
+ @Override
+ public V computeIfPresent(K key, BiFunction<? super K, ? super V, ? extends V> remappingFunction) {
+ Objects.requireNonNull(remappingFunction);
+
+ Entry<?,?> tab[] = table;
+ int hash = hash(key);
+ int index = (hash & 0x7FFFFFFF) % tab.length;
+ @SuppressWarnings("unchecked")
+ Entry<K,V> e = (Entry<K,V>) tab[index];
+ for(Entry<K,V> prev = null; e != null; prev = e, e = e.next) {
+ if (e.hash == hash && e.key.equals(key)) {
+ V newValue = remappingFunction.apply(key, e.value);
+ if (newValue == null) {
+ modCount++;
+ if (prev != null) {
+ prev.next = e.next;
+ } else {
+ tab[index] = e.next;
+ }
+ count--;
+ } else {
+ e.value = newValue;
+ }
+ return newValue;
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public V compute(K key, BiFunction<? super K, ? super V, ? extends V> remappingFunction) {
+ Objects.requireNonNull(remappingFunction);
+
+ Entry<?,?> tab[] = table;
+ int hash = hash(key);
+ int index = (hash & 0x7FFFFFFF) % tab.length;
+ @SuppressWarnings("unchecked")
+ Entry<K,V> e = (Entry<K,V>) tab[index];
+ for(Entry<K,V> prev = null; e != null; prev = e, e = e.next) {
+ if (e.hash == hash && Objects.equals(e.key, key)) {
+ V newValue = remappingFunction.apply(key, e.value);
+ if (newValue == null) {
+ modCount++;
+ if (prev != null) {
+ prev.next = e.next;
+ } else {
+ tab[index] = e.next;
+ }
+ count--;
+ } else {
+ e.value = newValue;
+ }
+ return newValue;
+ }
+ }
+
+ V newValue = remappingFunction.apply(key, null);
+ if (newValue != null) {
+ addEntry(hash, key, newValue, index);
+ }
+
+ return newValue;
+ }
+
+ @Override
+ public V merge(K key, V value, BiFunction<? super V, ? super V, ? extends V> remappingFunction) {
+ Objects.requireNonNull(remappingFunction);
+
+ Entry<?,?> tab[] = table;
+ int hash = hash(key);
+ int index = (hash & 0x7FFFFFFF) % tab.length;
+ @SuppressWarnings("unchecked")
+ Entry<K,V> e = (Entry<K,V>) tab[index];
+ for(Entry<K,V> prev = null; e != null; prev = e, e = e.next) {
+ if (e.hash == hash && e.key.equals(key)) {
+ V newValue = remappingFunction.apply(e.value, value);
+ if (newValue == null) {
+ modCount++;
+ if (prev != null) {
+ prev.next = e.next;
+ } else {
+ tab[index] = e.next;
+ }
+ count--;
+ } else {
+ e.value = newValue;
+ }
+ return newValue;
+ }
+ }
+
+ if (value != null) {
+ addEntry(hash, key, value, index);
+ }
+
+ return value;
+ }
+
/**
* Save the state of the Hashtable to a stream (i.e., serialize it).
*
* @serialData The <i>capacity</i> of the Hashtable (the length of the
* bucket array) is emitted (int), followed by the