public class Test{
public static void main(String[] args){
HashSet<String>set = new HashSet<>();
//调用HashSet无参构造方法,为HashSet类中HashMap类型的全局变量赋值
set.add("Tom");//map.put(e,PRESENT),HashSet添加元素实质是使用HashMap的key
set.add("Tom");//这里我们添加两次,在下面将会详细解释添加两次相同数据的具体过程
}
}
我们先在eclipse中用ctrl+左键点进add方法
再进一步点进put方法:
我们可以看到put方法返回的是putVal方法的返回值。
这里的hash(key)返回当前数据的hashCode码,就为下面putVal方法的hash值
接下来我们来看putVal方法,第一次添加Tom:
final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
boolean evict) {
Node<K,V>[] tab; Node<K,V> p; int n, i;
//这里的table是个数组类型的全局变量,初始值为null,所以我们第一次添加对象时tab = table都是null
if ((tab = table) == null || (n = tab.length) == 0)
//这里我们只需要知道,这里的resize(),是给table赋值,并且返回这个值,所以这里tab = table,n就是这个数组的长度
n = (tab = resize()).length;
//这个经过resize的数组,是一个长度为16,每一个元素都为null的数组,所以这个条件判断成立,tab[i]赋值成功,直接转到下面
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);
//这里最后返回null,这里返回null的话,add方法就返回true,添加成功!
return null;
}
然后我们来看第二次传入Tom:
final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
boolean evict) {
Node<K,V>[] tab; Node<K,V> p; int n, i;
//这里条件判断为false,因为已经添加了一个Tom在里面,代码已经运行过了一遍所以table不为null,n也不等0了,所以n的值不会改变,接着往下走
if ((tab = table) == null || (n = tab.length) == 0)
n = (tab = resize()).length;
//n的值既然没有改变,那么这里i也没有改变,这里的tab[i]已经被赋值过了,所以不为null,进入下方else
if ((p = tab[i = (n - 1) & hash]) == null)
tab[i] = newNode(hash, key, value, null);
else {
Node<K,V> e; K k;
//这里后面的hash就是String类型数据Tom的hashCode码,这里的p为tab[i],即添加的Tom,p.hash也是String类型数据Tom的hashCode码,两个码相同为true。再看后面,k和key都是"Tom",但地址不同,k=p.key为false;key!=null为true,key.equals(k)为trueh以整个条件判断为true
if (p.hash == hash &&
((k = p.key) == key || (key != null && key.equals(k))))
//于是这里e = p,这些同级的条件判断就跳过了
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(hac, key, value, null);e 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;
}
}
//由于e = p,p = tab[i],所以这里e不为null,条件判断为true
if (e != null) {// existing mapping for key
V oldValue = e.value;
if (!onlyIfAbsent || oldValue == null)
e.value = value;
afterNodeAccess(e);
//这里返回不为null,所以add方法为false,添加失败
return oldValue;
}
}
++modCount;
if (++size > threshold)
resize();
afterNodeInsertion(evict);
return null;
}
接下来,我们来使用HashSet的add方法来添加自定义类:
public class Test {e
public stateic void main(String[] args) {
HashSet<Student> set = new HashSet<>();
set.add(new Student("Tom"));
set.add(new Student("Tom"));
System.out.println(set.size());
}
}
class Student{
String id;
public Student(String id) {
this.id = id;
}
//这里的两个方法重写,我们下面分开来讨论
@Override
public int hashCode() {
return this.id.hashCode();
}
@Override
public boolean equals(Object obj) {
Student stu = (Student)obj;
return this.id.equals(stu.id);
}
h}
我们先看添h加第一个new Student(“Tom”);的情况:
final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
boolean evict) {
Node<K,V>[] tab; Node<K,V> p; int n, i;
//这里table为null,n为0,同上
if ((tab = table) == null || (n = tab.length) == 0)
//这里n有了一个值,table也变为了长度为16的数组,每个元素都是null
n = (tab = resize()).length;
//这里hash是用Object中的hashCode方法得出的,不过这里tab[i]还是为null
if ((p = tab[i = (n - 1) & hash]) == null)
c c//这里tab[i]赋值成功,跳过同级条件判断
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);
//返回null,则add方法返回true,添加成功
return null;
}
接下来,关键的来了!我们再添加一个new Student(“Tom”),先说没有重写方法的情况
final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
boolean evict) {
Node<K,V>[] tab; Node<K,V> p; int n, i;
//还是先走这里,这回table不为null了,长度为16,这个条件判断为false
if ((tab = table) == null || (n = tab.length) == 0)
n = (tab = resize()).length;
//这里的hash是一个关键哦,如果Student类中没有重写任何方法,那么这里的hash为Object中hashCode方法的返回值,为调用该方法数据的地址,所以这里==null是true!
if ((p = tab[i = (n - 1) & hash]) == null)
//所以这里tab[i]赋值成功,跳过同级条件判断
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,add方法return为true,添加成功
return null;
}
然后我们讨论在重写hashCode方法没有重写equals方法的情况下,添加第二个new Student(“Tom”):
final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
boolean evict) {
Node<K,V>[] tab; Node<K,V> p; int n, i;
//还是先走这里,为false
if ((tab = table) == null || (n = tab.length) == 0)
n = (tab = resize()).length;
//由于我们重写了hashCode方法,所以这里的i值和第一次添加时一样,这里条件判断也为false,走进下面else中
if ((p = tab[i = (n - 1) & hash]) == null)
tab[i] = newNode(hash, key, value, null);
else {
Node<K,V> e; K k;
//先走这,重写了hashCode,所以p.hash ==hash为true
//下面k == key为false,因为这是判断的地址,再看这个equals:如果我们没有重写equals方法,那么这里用的是Object中的equals方法,返回为false,因为比较的是地址,那就继续往下走
if (p.hash == hash &&
((k = p.key) == key || (key != null && key.equals(k))))
e = p;
//这里也为false,继续往下
else if (p instanceof TreeNode)
e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
//走到这里
else {
//这是个无限循环
for (int binCount = 0; ; ++binCount) {
//这里p.next为null,我们只添加了一个Student对象在里面,他的下一个位置一定为null
if ((e = p.next) == null) {
//这里给p.next赋值成果
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;
}
}
//e = p.next(),那时p.next还没赋值过,为null,这里e为null,为false
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;
}
最后,hashCode和equals方法都重写的情况
final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
boolean evict) {
Node<K,V>[] tab; Node<K,V> p; int n, i;
//false,不多解释
if ((tab = table) == null || (n = tab.length) == 0)
n = (tab = resize()).length;
//重写了hashCode为false
if ((p = tab[i = (n - 1) & hash]) == null)
tab[i] = newNode(hash, key, value, null);
else {
Node<K,V> e; K k;
//走这里,重写了hashCode所以p.hash == hash为true,key!=null为true,重写了equals方法所以key.equals(k))为true,整个条件判断为true
if (p.hash == hash &&
((k = p.key) == key || (key != null && key.equals(k))))
//所以e = p,跳过同级条件判断
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;
}
}
//e = p,所以这里为true
if (e != null) { // existing mapping for key
V oldValue = e.value;
if (!onlyIfAbsent || oldValue == null)
e.value = value;
afterNodeAccess(e);
//返回不为null,添加失败!
return oldValue;
}
}
++modCount;
if (++size > threshold)
resize();
afterNodeInsertion(evict);
return null;
}