1. remove

  • remove 有两个重载函数
  • V remove(Object key):移除指定 key 的元素,删除成功返回 value 值,没找到返回 null
  • boolean remove(Object key, Object value):移除指定 key 的元素,但对应key节点的value必须与 传入的value值相等,才会进行删除,返回值为 boolean
/* 可以看到,两个函数的区别就是 removeNode() 的第四个参数,与返回值类型不同 */
public V remove(Object key) {
    Node<K,V> e;
    /* removeNode: 移除指定 key 的 Node 节点 */
    return (e = removeNode(hash(key), key, null, false, true)) == null ?
        null : e.value;
}
@Override
publicboolean remove(Object key, Object value) {
    return removeNode(hash(key), key, value, true, true) != null;
}
  • removeNode 函数源码
/* matchValue:为true表示只有key,value值都相同是才删除,由于调用key删除,默认为false */
final Node<K,V> removeNode(int hash, Object key, Object value,
                               boolean matchValue, boolean movable) {
    /* tab:存储数据的数组, p: hash对应桶位的首元素, n:tab 数组的长度, index: hash对应的桶位下标 */                           
    Node<K,V>[] tab; Node<K,V> p; int n, index;
    /* tab不为空 && tab长度大于 0 && hash 值对应桶位首元素不为 null */
    if ((tab = table) != null && (n = tab.length) > 0 &&
        (p = tab[index = (n - 1) & hash]) != null) {
        /* node: 存储key对应的节点,默认为null,e: 用于循环的中继节点 */
        Node<K,V> node = null, e; K k; V v;
        /* 判断 p.key 是否与传入的 key 相等 */
        if (p.hash == hash &&
            ((k = p.key) == key || (key != null && key.equals(k))))
            node = p;
        /* 若 p.next == null 则该桶只有一个元素 */    
        else if ((e = p.next) != null) {
        	/* 判断 p 是否为树节点 */
            if (p instanceof TreeNode)
            	/* 树节点查找节点,可回看 02 篇 */
                node = ((TreeNode<K,V>)p).getTreeNode(hash, key);
            else {
            	/* 链节点查找 */
                do {
                	/* 判断key值是否相等 */
                    if (e.hash == hash &&
                        ((k = e.key) == key ||
                         (key != null && key.equals(k)))) {
                        node = e;
                        break; // 找到则退出当前循环
                    }
                    p = e;
                } while ((e = e.next) != null);
            }
        }
        /* node不为null && (!matchValue 默认为true,为false 继续判断value是否相等 ) */
        if (node != null && (!matchValue || (v = node.value) == value ||
                             (value != null && value.equals(v)))) {
            /* 若 node 为树节点, 调用 TreeNode 的移除节点,回看 02 篇 */
            if (node instanceof TreeNode)
                ((TreeNode<K,V>)node).removeTreeNode(this, tab, movable);
            /* 若 node不为树节点且为 头结点, 将桶位首元素指向 next 即可 */
            else if (node == p)
                tab[index] = node.next;
            /* 链表删除,由于是单向链表,p.next 指向 node.next 即可 */
            else
                p.next = node.next;
            /* 更新 modCount,size */    
            ++modCount;
            --size;
            /* 预留函数 */
            afterNodeRemoval(node);
            /* 返回删除的节点 */
            return node;
        }
    }
    /* 找不到返回 null */
    return null;
}

2. clear()

  • 清空所有元素
/* 循环tab的每个桶位,将首元素置为null */
public void clear() {
    Node<K,V>[] tab;
    modCount++;
    if ((tab = table) != null && size > 0) {
        size = 0;
        for (int i = 0; i < tab.length; ++i)
            tab[i] = null;
    }
}

3. putIfAbsent

  • 添加新元素,若key已存在,不替换,除非值为 null,详解 03 篇,这里不做赘述
@Override
publicV putIfAbsent(K key, V value) {
	/* param4: onlyIfAbsent 为true 即不替换 */
    return putVal(hash(key), key, value, true, true);
}
/*  putVal找到该元素的源码
if (!onlyIfAbsent || oldValue == null)
     e.value = value;
*/

4. 替换函数 replace()

  • replace有两个重载函数
  • replace(K key, V oldValue, V newValue):找到key对应的节点后,还需比对 oldValue 才能进行替换,返回值为 boolean
  • replace(K key, V value):找到key对应的节点之后,直接替换值,并返回旧值,简单来说,就是不要验证身份,返回值为 oldValue 。
@Override
public boolean replace(K key, V oldValue, V newValue) {
    Node<K,V> e; V v;
    /* 找到 key 对应的节点 && value 与 oldValue 相等,才置换为 新值 */
    if ((e = getNode(hash(key), key)) != null &&
        ((v = e.value) == oldValue || (v != null && v.equals(oldValue)))) {
        e.value = newValue;
        /* 预留函数 */
        afterNodeAccess(e);
        return true;
    }
    /* 没找到返回 false */
    return false;
}

@Override
public V replace(K key, V value) {
    Node<K,V> e;
    /* 找到节点,直接替换为新值,返回旧值 */
    if ((e = getNode(hash(key), key)) != null) {
        V oldValue = e.value;
        e.value = value;
        /* 预留函数 */
        afterNodeAccess(e);
        return oldValue;
    }
    return null;
}

5. 批量替换函数 replaceAll()

  • 给定执行动作function,替换所有节点的值
@Override
public void replaceAll(BiFunction<? super K, ? super V, ? extends V> function) {
    Node<K,V>[] tab;
    /* 执行动作 null 判断 */
    if (function == null)
        throw new NullPointerException();
    /* 元素个数 > 0 && tab 不为空 */   
    if (size > 0 && (tab = table) != null) {
    	/* 记录当前修改版本号 */
        int mc = modCount;
        /* 双层遍历,桶位与链,直至所有元素执行完指定操作 */
        for (int i = 0; i < tab.length; ++i) {
            for (Node<K,V> e = tab[i]; e != null; e = e.next) {
                e.value = function.apply(e.key, e.value);
            }
        }
        /* 修改当前版本号,但是目前value值已经替换完毕 */
        if (modCount != mc)
            throw new ConcurrentModificationException();
    }
}

6. 总结

  • 注意一点,replaceAll 报错ConcurrentModificationException,但此时值已经改变完。
import java.util.HashMap;

public class HashMapTest02 {
    public static void main(String[] args) {
        HashMap<String, Integer> hashMap = new HashMap<>();
        for(int i = 1; i <= 10; i++) {
            hashMap.put("我是" + i, i);
        }
        System.out.println("now hashMap: " + hashMap);

        System.out.println("hashMap.remove('我是1'): " + hashMap.remove("我是1"));
        System.out.println("hashMap.remove('我是2', 2): " + hashMap.remove("我是2",2));
        System.out.println("hashMap.remove('我是3', 30): " + hashMap.remove("我是3",30));
        System.out.println("now hashMap: " + hashMap);
        System.out.println("==============================");
        System.out.println("hashMap.putIfAbsent('我是11', 11): " + hashMap.putIfAbsent("我是11", 11));
        System.out.println("hashMap.putIfAbsent('我是10', 100): " + hashMap.putIfAbsent("我是10", 100));
        System.out.println("now hashMap: " + hashMap);
        System.out.println("==============================");
        System.out.println("hashMap.replace('我是5', 50): " + hashMap.replace("我是5", 50));
        System.out.println("hashMap.replace('我是6,', 6, 60): " + hashMap.replace("我是6", 6, 60));
        System.out.println("hashMap.replace('我是7,', 17, 70): " + hashMap.replace("我是7", 17, 70));
        System.out.println("now hashMap: " + hashMap);
        System.out.println("==============================");
        new Thread(() -> {
            try {
                Thread.sleep(100);
                hashMap.put("我是13", 13);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }).start();
        try {
            hashMap.replaceAll((k, v) -> {
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                v = v * 10;
                return v;
            });
        }catch (Exception e) {
            e.printStackTrace();
        }
        System.out.println("now hashmap: " + hashMap);
        hashMap.clear();
        System.out.println("now hashmap: " + hashMap);

    }
}

java map update修改成功返回值 map remove返回值_ide