1.分析add()方法底层代码:
package add;
import java.util.HashSet;
public class Test {
public static void main(String[] args) {
HashSet<String> set = new HashSet<String>();
set.add("Tom"); //调用HashSet add方法,map.put(e, PRESENT),map为全局变量,map指向的是创建HashSet对象时创建的HashMap对象
set.add("Tom");//思考:怎么判断不允许重复?
HashSet<String> set = new HashSet<String>();
创建HashSet对象,调用了该类中无参构造方法,执行了该构造方法中map = new HashMap<>();,map为HashSet全局变量,蓝色大写的是常量.
map为一个全局变量。
首先分析hashCode();
package add;
import java.util.HashSet;
public class Test {
public static void main(String[] args) {
Test test = new Test();
System.out.println(test.hashCode()); //输出对应于对象的某一个数
System.out.println(test); //默认调用toString方法
System.out.println(test.toString()); //输出对象的地址
}
}
package add;
import java.util.HashSet;
public class Test {
public static void main(String[] args) {
String name1 = "tom";
String name2 = "tom";
System.out.println(name1.hashCode());
System.out.println(name2.hashCode());
}
}
但是如果创建对象了,就是不同的结果。
package add;
import java.util.HashSet;
public class Test {
public static void main(String[] args) {
Test test = new Test();
Test test1 = new Test();
System.out.println(test.hashCode());
System.out.println(test1.hashCode());
}
}
package add;
import java.util.HashSet;
public class Test {
public static void main(String[] args) {
Test test = new Test();
System.out.println(test.hashCode());
test = new Test(); //新建一个对象赋值给test,所以俩次结果是不一样的
System.out.println(test1.hashCode());
}
}
结论:不同对象的hashCode一定不相同;相同对象的HashCode的值一定相同,所以HashMap中hash方法:传入相同的对象,得到相同的结果。
2.以第一个代码块分析add()底层代码:
public boolean add(E e) {
return map.put(e, PRESENT)==null;
}
public V put(K key, V value) {
return putVal(hash(key), key, value, false, true);
}
static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
这时的key就是:“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;
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;
}
第一次添加“tom”; if ((tab = table) == null || (n = tab.length) == 0)
table是全局变量,并没有给全局变量赋值,
所以table=null。
n = (tab = resize()).length;
resize() 返回一个数组,在其中给全局变量table赋值,就是与table指向同一个地址 ,而且方法返回newtab,赋值给tab,所以table与tab也指向同一个地址
if ((p = tab[i = (n - 1) & hash]) == null)
n=16
tab[i] = newNode(hash, key, value, null);
添加第一个Tom tab[i],tab与table指向同一个地址,所以table[i]也是有值的,且相同。
在第二次添加“tom”;时,
if ((tab = table) == null || (n = tab.length) == 0)
n = (tab = resize()).length;
因为table是全局变量,第一次添加"tom";已经有值了,所以第二次的时候不是空,因为这俩行代码就不再执行了
根据if ((p = tab[i = (n - 1) & hash]) == null)
执行else
在else中执行代码:
if (p.hash == hash &&
((k = p.key) == key || (key != null && key.equals(k))))
e = p;
最后,因为
if (e != null) {
V oldValue = e.value;
if (!onlyIfAbsent || oldValue == null)
e.value = value;
afterNodeAccess(e);
return oldValue;
}
e中有第二次传过来的对象,所以将这次value的值覆盖前一次的值,最后返回方法。所以这一次,就没有添加成功。