HashMap1.8 源码解析(2)--删除元素

完整代码:代码

前言

有了上一篇的基础HashMap1.8 源码解析(1)--插入元素 ,相信这一篇会很简单

删除

删除有两种方式:

1. remove(Object key)根据key删除
2. remove(Object key, Object value) 根据keyvalue删除

注意:只有扩容,没有说删除后缩小容量这个操作.

    /**
     * 注意与remove(Object key)不同的两点:
     * 1. 要根据key,value同时符合才可以删除该节点
     * 2. 返回值是true   表明正确删除
     *    返回值是false  表明没有这个节点
     */
    @Override
    public boolean remove(Object key, Object value) {
        return removeNode(hash(key), key, value, true, true) != null;
    }
    
    /**
     * 根据key来remove在HashMap中的节点
     */
    public V remove(Object key) {
        Node e;
        return (e = removeNode(hash(key), key, null, false, true)) == null ?
            null : e.value;
    }
    
    /**
     * 
     * @param hash            hash值
     * @param key             
     * @param value
     * @param matchValue    如果true表明必须key和value同时符合要求才可以删除,如果false的话只需要key符合就可以
     * @param movable       用于红黑树中的操作 在红黑树分析中会有提及  
     * @return 如果存在这个节点,会返回此节点 否则返回null
     */
    final Node removeNode(int hash, Object key, Object value, boolean matchValue, boolean movable) {
        Node[] tab;
        Node p;
        int n, index;
        /**
         *  如果hash值对应的bucket存在的话就继续寻找
         *  分两步:
         *  1. 先寻找到节点,如果存在则放到node中
         *  2. 如果存在删除该节点
         */
        
        // 第一步 查找节点
        if ((tab = table) != null && (n = tab.length) > 0 && (p = tab[index = (n - 1) & hash]) != null) {
            Node node = null, e;
            K k;
            V v;
            /**
             * 如果存在这个节点,会把节点赋值给node
             * 分三种情况:
             * 1. 就在hash值对应的bucket中是第一个元素
             * 2. 在红黑树中
             * 3. 在链表中
             */
            if (p.hash == hash && ((k = p.key) == key || (key != null && key.equals(k))))
                node = p;                          // 情况1
            else if ((e = p.next) != null) {      
                if (p instanceof TreeNode)         // 情况2  会在HashMap红黑树中专门分析
                    node = ((TreeNode) p).getTreeNode(hash, key);
                else {                             // 情况3
                    do {                           // 循环链表查找 查找到后赋值给node
                        if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k)))) {
                            node = e;
                            break;
                        }
                        p = e;
                    } while ((e = e.next) != null);
                }
            }
            /**
             * 第二步:删除操作
             * 三种情况:
             * 1. 树节点 
             * 2. bucket节点,直接bucket节点设置为node.next
             * 3. 在链表中删除node节点
             * 
             * modCount会在分析HashMap Iterator中详细分析
             */
            if (node != null && (!matchValue || (v = node.value) == value || (value != null && value.equals(v)))) {
                if (node instanceof TreeNode)
                    ((TreeNode) node).removeTreeNode(this, tab, movable);
                else if (node == p)
                    tab[index] = node.next;
                else
                    p.next = node.next;
                ++modCount;
                --size;
                afterNodeRemoval(node); //留作子类LinkedHashMap中使用
                return node;
            }
        }
        return null;
    }
小例子

这个例子是为了说明一下两个remove之间的区别

还是和上篇一样定义了一个Person

public class Person {
    String name;
    int age;
    public Person() {}
    public Person(String name, int age) {
        this.name = name;
        this.age  = age;
    }
    
    @Override
    public String toString() {
        return "Person [name=" + name + ", age=" + age + "]";
    }
    @Override
    public boolean equals(Object obj) {

        if (this == obj) return true;
        if (obj instanceof Person) {
            Person p = (Person)obj;
            return p.name.equals(this.name);
        }
        return false;

        //return true;
    }
    @Override
    public int hashCode() {
        // TODO Auto-generated method stub
        return age;
    }
}

测试

public class TestHashMap {

    public static void main(String[] args) {
        HashMap map = new HashMap<>(3);
        map.put(new Person("tom_1", 12), 12);
        map.put(new Person("tom_2", 0), 0);
        map.put(new Person("tom_3", 4), 4);
        System.out.println("capacity:" + map.table.length);
        //printMap(map);
        map.put(new Person("tom_4", 16), 16);
        //System.out.println("------------------after insert tom_4---------------------");
        System.out.println("capacity:" + map.table.length);
        printMap(map);
        System.out.println("---------------------------------------------------------");    
        
        boolean f = map.remove(new Person("tom_4", 4), 4); //没有删除成功 因为必须key和value都要对应上才可以删除
        System.out.println("delete : " + f);
        printMap(map);
        System.out.println("---------------------------------------------------------");        
        map.remove(new Person("tom_4", 16));
        map.remove(new Person("tom_3", 4));
        map.remove(new Person("tom_2", 0));
        map.remove(new Person("tom_1", 12));
        printMap(map);
    }
    
    private static void printMap(HashMap map) {
        HashMap.Node[] table = map.table; 
        for (int i = 0; i < table.length; i++) {
            System.out.print(i + ":");
            HashMap.Node e;
            if ((e = table[i]) != null) {
                System.out.print(e);
                HashMap.Node p;
                while ((p = e.next) != null) {
                    System.out.print("->" + p);
                    e = e.next;
                }
            }
            System.out.println();
        }
    }
}

结果


HashMap1.8 源码解析(2)--删除元素_第1张图片
image.png

1.HashMap1.8 源码解析(1)--插入元素
2.HashMap1.8 源码解析(2)--删除元素
3.HashMap1.8 源码解析(3)--TreeNode(红黑树)包括每一个方法
4.HashMap1.8 源码解析(4)--遍历元素

参考

1.java1.8 源码

你可能感兴趣的:(HashMap1.8 源码解析(2)--删除元素)