src/share/classes/java/util/Hashtable.java

Print this page
rev 6878 : 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 **** /* ! * Copyright (c) 1994, 2012, 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 --- 1,7 ---- /* ! * 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,32 **** --- 22,36 ---- * 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,462 **** --- 457,486 ---- 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,514 **** 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++; return null; } /** * Removes the key (and its corresponding value) from this --- 514,524 ---- entry.value = value; return old; } } ! addEntry(hash, key, value, index); return null; } /** * Removes the key (and its corresponding value) from this
*** 890,899 **** --- 900,1142 ---- 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> action) { + Objects.requireNonNull(action); // explicit check required in case + // table is empty. + Entry<?,?>[] tab = table; + for (Entry<?,?> entry : tab) { + while (entry != null) { + action.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