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

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

add()方法

首先我们看下这个代码

 package com.xt.set;

 import java.util.HashSet;
import java.util.Iterator;
import java.util.Set; public class AddTest { public static void main(String[] args) {
Set<Object> names = new HashSet<>();
names.add(new Student("111"));
names.add(new Student("111"));
System.out.println(names.size());
}
}

如果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方法原理相同


                                    

上一篇:JS复制内容到剪贴板: 兼容IE、Firefox、Chrome、Safari所有浏览器【转】


下一篇:unittest框架