• HashSet——add remove contains方法底层代码分析(hashCode equals 方法的重写)


    引言:我们都知道HashSet这个类有add   remove   contains方法,但是我们要深刻理解到底是怎么判断它是否重复加入了,什么时候才移除,什么时候才算是包括?????????

    add()方法

    首先我们看下这个代码

     1 package com.xt.set;
     2 
     3 import java.util.HashSet;
     4 import java.util.Iterator;
     5 import java.util.Set;
     6 
     7 public class AddTest {
     8 
     9     public static void main(String[] args) {
    10         Set<Object> names = new HashSet<>();
    11         names.add(new Student("111"));
    12         names.add(new Student("111"));
    13         System.out.println(names.size());
    14     }
    15 }

    如果Student类是下面的

    package com.xt.set;
    
    public class Student {
    
        private String id;
    
        
        public Student(String id) {
            this.id = id;
        }
    
        
    }

    输出结果为2,为什么呢,按照我们的思路不应该是1吗?

    这样我们 按住ctrl键点击add方法进入到HashSet.class类中的add方法

     public boolean add(E e) {
            return map.put(e, PRESENT)==null;
        }

    继续点击put方法进入到HashMap类中的下面两个方法

     public V put(K key, V value) {
            return putVal(hash(key), key, value, false, true);
        }
    
     
        final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
                       boolean evict) {
            Node<K,V>[] tab; Node<K,V> p; int n, i;
            if ((tab = table) == null || (n = tab.length) == 0)
                n = (tab = resize()).length;
            if ((p = tab[i = (n - 1) & hash]) == null)
                tab[i] = newNode(hash, key, value, null);
            else {
                Node<K,V> e; K k;
                if (p.hash == hash &&
                    ((k = p.key) == key || (key != null && key.equals(k))))
                    e = p;
                else if (p instanceof TreeNode)
                    e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
                else {
                    for (int binCount = 0; ; ++binCount) {
                        if ((e = p.next) == null) {
                            p.next = newNode(hash, key, value, null);
                            if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
                                treeifyBin(tab, hash);
                            break;
                        }
                        if (e.hash == hash &&
                            ((k = e.key) == key || (key != null && key.equals(k))))
                            break;
                        p = e;
                    }
                }
                if (e != null) { // existing mapping for key
                    V oldValue = e.value;
                    if (!onlyIfAbsent || oldValue == null)
                        e.value = value;
                    afterNodeAccess(e);
                    return oldValue;
                }
            }
            ++modCount;
            if (++size > threshold)
                resize();
            afterNodeInsertion(evict);
            return null;
        }

    我们看上面标红色的代码。发现他重新加入时判定的标准1:判定两个实例化对象的HashCode()值是否相同,如果相同

                              2:判定他们的地址是否相同,如果相同就是同一个实例对象。如果不相同

                              3:执行equals方法。这个时候的equals方法是Object中的equals方法,比较的是地址

                              如:

                            

     public boolean equals(Object obj) {
            return (this == obj);
        }

    按照这个思路的话,我们分析,new Student("111")和 new Student("111")  因为只是两个对象,,那么他们的HashCode()值不是相同的,直接加入了

    那么我们来重写hashCode()和equals()方法

    package com.xt.set;
    
    public class Student {
    
        private String id;
    
        
        public Student(String id) {
            this.id = id;
        }
    
        @Override
        public int hashCode() {
            System.out.println(this.id+"hashCode"+this.id.hashCode());
            return this.id.hashCode();
        }
    
        @Override
        public boolean equals(Object obj) {
            System.out.println("equals");
            if(this instanceof Student&&obj instanceof Student) {
                return this.id.equals(((Student)obj).id);
            }
            return false;
        }
        
        
    }

    这个时候HashCode()方法,比较的是id字符串的HashCode值,相同字符串的HashCode的值是相同的,两个相同字符串的地址(“111”==“111”是相同的   new String(“111”)==new String(“111”)是不相同的),equals方法现在比较的字符串本身是否相同,这个时候就是重复的两个值,会保存一个  输出结果为  1

    remove()方法

    final Node<K,V> removeNode(int hash, Object key, Object value,
                                   boolean matchValue, boolean movable) {
            Node<K,V>[] tab; Node<K,V> p; int n, index;
            if ((tab = table) != null && (n = tab.length) > 0 &&
                (p = tab[index = (n - 1) & hash]) != null) {
                Node<K,V> node = null, e; K k; V v;
                if (p.hash == hash &&
                    ((k = p.key) == key || (key != null && key.equals(k))))
                    node = p;
                else if ((e = p.next) != null) {
                    if (p instanceof TreeNode)
                        node = ((TreeNode<K,V>)p).getTreeNode(hash, key);
                    else {
                        do {
                            if (e.hash == hash &&
                                ((k = e.key) == key ||
                                 (key != null && key.equals(k)))) {
                                node = e;
                                break;
                            }
                            p = e;
                        } while ((e = e.next) != null);
                    }
                }
                if (node != null && (!matchValue || (v = node.value) == value ||
                                     (value != null && value.equals(v)))) {
                    if (node instanceof TreeNode)
                        ((TreeNode<K,V>)node).removeTreeNode(this, tab, movable);
                    else if (node == p)
                        tab[index] = node.next;
                    else
                        p.next = node.next;
                    ++modCount;
                    --size;
                    afterNodeRemoval(node);
                    return node;
                }
            }
            return null;
        }
    
       

    看红色代码 ,发现和add方法原理相同

    contains()方法

    final Node<K,V> getNode(int hash, Object key) {
            Node<K,V>[] tab; Node<K,V> first, e; int n; K k;
            if ((tab = table) != null && (n = tab.length) > 0 &&
                (first = tab[(n - 1) & hash]) != null) {
                if (first.hash == hash && // always check first node
                    ((k = first.key) == key || (key != null && key.equals(k))))
                    return first;
                if ((e = first.next) != null) {
                    if (first instanceof TreeNode)
                        return ((TreeNode<K,V>)first).getTreeNode(hash, key);
                    do {
                        if (e.hash == hash &&
                            ((k = e.key) == key || (key != null && key.equals(k))))
                            return e;
                    } while ((e = e.next) != null);
                }
            }
            return null;
        }

    看红色代码 ,发现和add方法原理相同

    
    

                                        

  • 相关阅读:
    【JDK源码】从源码看公平锁和非公平锁得区别
    【spring源码解读】spring加载流程refresh之prepareBeanFactory(beanFactory)
    【JDK源码】Synchronized关键字原理,和锁的膨胀过程
    【Spring源码解读】BeanPostProcessor 接口解读
    【spring源码】spring的循环依赖
    JS-04 JS中的函数都是按值传递的
    CSS-03 queue方法
    CSS-02 BFC的理解
    CSS-01 CSS代码标准和规范
    JS-03 牛客网练习
  • 原文地址:https://www.cnblogs.com/lyxcode/p/9466789.html
Copyright © 2020-2023  润新知