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

Posted lyxcode

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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方法原理相同


                                    

 

以上是关于HashSet——add remove contains方法底层代码分析(hashCode equals 方法的重写)的主要内容,如果未能解决你的问题,请参考以下文章

HashSet源码解析

HashSet源码解析

HashSet

程序输出解释HashSet [重复]

Java8集合框架——HashSet源码分析

HashSet和HashMap的区别