src/share/classes/java/util/HashMap.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 +1,7 @@
 /*
- * Copyright (c) 1997, 2012, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 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,15 @@
  * or visit www.oracle.com if you need additional information or have any
  * questions.
  */
 
 package java.util;
+
 import java.io.*;
+import java.util.function.Consumer;
+import java.util.function.BiFunction;
+import java.util.function.Function;
 
 /**
  * Hash table based implementation of the <tt>Map</tt> interface.  This
  * implementation provides all of the optional map operations, and permits
  * <tt>null</tt> values and the <tt>null</tt> key.  (The <tt>HashMap</tt>

@@ -374,10 +378,17 @@
         Entry<K,V> entry = getEntry(key);
 
         return null == entry ? null : entry.getValue();
     }
 
+    @Override
+    public V getOrDefault(Object key, V defaultValue) {
+        Entry<K,V> entry = getEntry(key);
+
+        return (entry == null) ? defaultValue : entry.getValue();
+    }
+
     /**
      * Returns <tt>true</tt> if this map contains a mapping for the
      * specified key.
      *
      * @param   key   The key whose presence in this map is to be tested

@@ -601,10 +612,265 @@
     public V remove(Object key) {
         Entry<K,V> e = removeEntryForKey(key);
         return (e == null ? null : e.value);
     }
 
+    // optimized implementations of default methods in Map
+
+    @Override
+    public V putIfAbsent(K key, V value) {
+        if (table == EMPTY_TABLE) {
+            inflateTable(threshold);
+        }
+        int hash = (key == null) ? 0 : hash(key);
+        int i = indexFor(hash, table.length);
+        @SuppressWarnings("unchecked")
+        Entry<K,V> e = (Entry<K,V>)table[i];
+        for(; e != null; e = e.next) {
+            if (e.hash == hash && Objects.equals(e.key, key)) {
+                if(e.value != null) {
+                    return e.value;
+                }
+                e.value = value;
+                modCount++;
+                e.recordAccess(this);
+                return null;
+            }
+        }
+
+        modCount++;
+        addEntry(hash, key, value, i);
+        return null;
+    }
+
+    @Override
+    public boolean remove(Object key, Object value) {
+        if (isEmpty()) {
+            return false;
+        }
+        int hash = (key == null) ? 0 : hash(key);
+        int i = indexFor(hash, table.length);
+        @SuppressWarnings("unchecked")
+        Entry<K,V> prev = (Entry<K,V>)table[i];
+        Entry<K,V> e = prev;
+
+        while (e != null) {
+            Entry<K,V> next = e.next;
+            if (e.hash == hash && Objects.equals(e.key, key)) {
+                if (!Objects.equals(e.value, value)) {
+                    return false;
+                }
+                modCount++;
+                size--;
+                if (prev == e)
+                    table[i] = next;
+                else
+                    prev.next = next;
+                e.recordRemoval(this);
+                return true;
+            }
+            prev = e;
+            e = next;
+        }
+
+        return false;
+    }
+
+    @Override
+    public boolean replace(K key, V oldValue, V newValue) {
+        if (isEmpty()) {
+            return false;
+        }
+        int hash = (key == null) ? 0 : hash(key);
+        int i = indexFor(hash, table.length);
+        @SuppressWarnings("unchecked")
+        Entry<K,V> e = (Entry<K,V>)table[i];
+        for (; e != null; e = e.next) {
+            if (e.hash == hash && Objects.equals(e.key, key) && Objects.equals(e.value, oldValue)) {
+                e.value = newValue;
+                e.recordAccess(this);
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    @Override
+    public V replace(K key, V value) {
+        if (isEmpty()) {
+            return null;
+        }
+        int hash = (key == null) ? 0 : hash(key);
+        int i = indexFor(hash, table.length);
+        @SuppressWarnings("unchecked")
+        Entry<K,V> e = (Entry<K,V>)table[i];
+        for (; e != null; e = e.next) {
+            if (e.hash == hash && Objects.equals(e.key, key)) {
+                V oldValue = e.value;
+                e.value = value;
+                e.recordAccess(this);
+                return oldValue;
+            }
+        }
+
+        return null;
+    }
+
+    @Override
+    public V computeIfAbsent(K key, Function<? super K, ? extends V> mappingFunction) {
+        if (table == EMPTY_TABLE) {
+            inflateTable(threshold);
+        }
+        int hash = (key == null) ? 0 : hash(key);
+        int i = indexFor(hash, table.length);
+        @SuppressWarnings("unchecked")
+        Entry<K,V> e = (Entry<K,V>)table[i];
+        for (; e != null; e = e.next) {
+            if (e.hash == hash && Objects.equals(e.key, key)) {
+                V oldValue = e.value;
+                return oldValue == null ? (e.value = mappingFunction.apply(key)) : oldValue;
+            }
+        }
+
+        V newValue = mappingFunction.apply(key);
+        if (newValue != null) {
+            modCount++;
+            addEntry(hash, key, newValue, i);
+        }
+
+        return newValue;
+    }
+
+    @Override
+    public V computeIfPresent(K key, BiFunction<? super K, ? super V, ? extends V> remappingFunction) {
+        if (isEmpty()) {
+            return null;
+        }
+        int hash = (key == null) ? 0 : hash(key);
+        int i = indexFor(hash, table.length);
+        @SuppressWarnings("unchecked")
+        Entry<K,V> prev = (Entry<K,V>)table[i];
+        Entry<K,V> e = prev;
+
+        while (e != null) {
+            Entry<K,V> next = e.next;
+            if (e.hash == hash && Objects.equals(e.key, key)) {
+                V oldValue = e.value;
+                if (oldValue == null)
+                    break;
+                V newValue = remappingFunction.apply(key, oldValue);
+                modCount++;
+                if (newValue == null) {
+                    size--;
+                    if (prev == e)
+                        table[i] = next;
+                    else
+                        prev.next = next;
+                    e.recordRemoval(this);
+                } else {
+                    e.value = newValue;
+                    e.recordAccess(this);
+                }
+                return newValue;
+            }
+            prev = e;
+            e = next;
+        }
+
+        return null;
+    }
+
+    @Override
+    public V compute(K key, BiFunction<? super K, ? super V, ? extends V> remappingFunction) {
+        if (table == EMPTY_TABLE) {
+            inflateTable(threshold);
+        }
+        int hash = (key == null) ? 0 : hash(key);
+        int i = indexFor(hash, table.length);
+        @SuppressWarnings("unchecked")
+        Entry<K,V> prev = (Entry<K,V>)table[i];
+        Entry<K,V> e = prev;
+
+        while (e != null) {
+            Entry<K,V> next = e.next;
+            if (e.hash == hash && Objects.equals(e.key, key)) {
+                V oldValue = e.value;
+                V newValue = remappingFunction.apply(key, oldValue);
+                if (newValue != oldValue) {
+                    modCount++;
+                    if (newValue == null) {
+                        size--;
+                        if (prev == e)
+                            table[i] = next;
+                        else
+                            prev.next = next;
+                        e.recordRemoval(this);
+                    } else {
+                        e.value = newValue;
+                        e.recordAccess(this);
+                    }
+                }
+                return newValue;
+            }
+            prev = e;
+            e = next;
+        }
+
+        V newValue = remappingFunction.apply(key, null);
+        if (newValue != null) {
+            modCount++;
+            addEntry(hash, key, newValue, i);
+        }
+
+        return newValue;
+    }
+
+    @Override
+    public V merge(K key, V value, BiFunction<? super V, ? super V, ? extends V> remappingFunction) {
+        if (table == EMPTY_TABLE) {
+            inflateTable(threshold);
+        }
+        int hash = (key == null) ? 0 : hash(key);
+        int i = indexFor(hash, table.length);
+        @SuppressWarnings("unchecked")
+        Entry<K,V> prev = (Entry<K,V>)table[i];
+        Entry<K,V> e = prev;
+
+        while (e != null) {
+            Entry<K,V> next = e.next;
+            if (e.hash == hash && Objects.equals(e.key, key)) {
+                V oldValue = e.value;
+                V newValue = remappingFunction.apply(oldValue, value);
+                modCount++;
+                if (newValue == null) {
+                    size--;
+                    if (prev == e)
+                        table[i] = next;
+                    else
+                        prev.next = next;
+                    e.recordRemoval(this);
+                } else {
+                    e.value = newValue;
+                    e.recordAccess(this);
+                }
+                return newValue;
+            }
+            prev = e;
+            e = next;
+        }
+
+        if (value != null) {
+            modCount++;
+            addEntry(hash, key, value, i);
+        }
+
+        return value;
+    }
+
+    // end of optimized implementations of default methods in Map
+
     /**
      * Removes and returns the entry associated with the specified key
      * in the HashMap.  Returns null if the HashMap contains no mapping
      * for this key.
      */

@@ -695,24 +961,24 @@
     public boolean containsValue(Object value) {
         if (value == null)
             return containsNullValue();
 
         Entry<?,?>[] tab = table;
-        for (int i = 0; i < tab.length ; i++)
-            for (Entry<?,?> e = tab[i] ; e != null ; e = e.next)
+        for (int i = 0; i < tab.length; i++)
+            for (Entry<?,?> e = tab[i]; e != null; e = e.next)
                 if (value.equals(e.value))
                     return true;
         return false;
     }
 
     /**
      * Special-case code for containsValue with null argument
      */
     private boolean containsNullValue() {
         Entry<?,?>[] tab = table;
-        for (int i = 0; i < tab.length ; i++)
-            for (Entry<?,?> e = tab[i] ; e != null ; e = e.next)
+        for (int i = 0; i < tab.length; i++)
+            for (Entry<?,?> e = tab[i]; e != null; e = e.next)
                 if (e.value == null)
                     return true;
         return false;
     }