HashSet中add()方法的执行过程分析
一、在HashSet中添加一个String类型的数据,具体步骤如下:
HashSet<String> set = new HashSet<>();
set.add("Tom");//HashSet存储的数据实际上存在了HashMap的key中 //(1)
set.add("Tom");//(2)
set.add("Lucy");//(3)
二、执行过程的步骤
1、创建一个HashSet类
HashSet<String> set = new HashSet<>();
2、在HashSet中创建一个map对象
public HashSet() {
map = new HashMap<>();
}
3、将在HashSet中添加的数据存储到HashMap的key中,add(E e)为HashSet类中的方法
public boolean add(E e) {
return map.put(e, PRESENT)==null;//返回put方法
}
4、put(K key, V value)为HashMap类中的方法
public V put(K key, V value) {
return putVal(hash(key), key, value, false, true);
}
5、主要执行过程
(1)执行set.add(“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;//创建一个putValue方法,输入hash(key)的int类型数据,key的String类型数据等数据
if ((tab = table) == null || (n = tab.length) == 0)
/*
*1、为什么成员变量table为null?因为成员变量table默认值是null(transient Node<K,V>[] table);,且在之后的的所有构造方法中未给table赋值,因此table为null;
*2、此前创建的全局变量table未赋值,因此为null( transient Node<K,V>[] table;),因此(tab = table) == null 成立,继续执行下一条语句;
*/
n = (tab = resize()).length;//resize()为创建一个重置数组的方法,首先使tab与table指向同一个地址,其次该地址上有一个数组resize(),因此tab表示该数组。该数组初始长度为16,tab=resize()使数组中每个元素都为null,n获取了该数组的长度,继续执行下一条语句;
if ((p = tab[i = (n - 1) & hash]) == null)//此时hash即为"Tom","i = (n - 1) & hash"表示i为n-1按位与hash的随机的一个角标,由于tab中所有元素都为null,不可能和hash相同,因此(p = tab[i = (n - 1) & hash]) == null成立,继续执行下一条语句;
tab[i] = newNode(hash, key, value, null);//将数据存入tab[i]中,且由于tab和table指向一个地址,因此数据也存入到了table中;
//(中间不执行语句省略)
if (++size > threshold)
resize();
afterNodeInsertion(evict);
return null;
}
(2)执行set.add(“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;//创建一个putValue方法,输入hash(key)的int类型数据,key的String类型数据等数据
if ((tab = table) == null || (n = tab.length) == 0)//由于上一步执行过程中tab数组中存入了第一个"Tom"数据,则(tab = table)不为null,且(n = tab.length)必然不为零,所以if条件不成立,直接执行下一个if语句;
n = (tab = resize()).length;
if ((p = tab[i = (n - 1) & hash]) == null)//该条件中hash为第一个"Tom"存储的位置这个"i"与上一步执行过程的"i"相同,即tab[i]已经有了"Tom"数据,因此p= tab[i = (n - 1) & hash])不为null,执行else语句;
tab[i] = newNode(hash, key, value, null);
else {
Node<K,V> e; K k;//创建Node对象
if (p.hash == hash &&
((k = p.key) == key || (key != null && key.equals(k))))//由于p.hash=tab[i]存储"Tom"数据,因此p.hash=hash;又因为p.key为String类型"Tom",与key相同,因此if条件成立,执行下一条语句;
e = p;
//(中间不执行语句省略)
if (e != null) { // existing mapping for key //e不为null,执行下一条语句
V oldValue = e.value;
if (!onlyIfAbsent || oldValue == null)
e.value = value;
afterNodeAccess(e);
return oldValue;//返回原值,即"Tom"数据
}
}
++modCount;
if (++size > threshold)
resize();
afterNodeInsertion(evict);
return null;
}
(3)执行set.add(“Lucy”);
final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
boolean evict) {//创建一个putValue方法,输入hash(key)的int类型数据,key的String类型数据等数据
Node<K,V>[] tab; Node<K,V> p; int n, i;
if ((tab = table) == null || (n = tab.length) == 0)//由于此前执行过程中tab数组中存入了第一个"Tom"数据,则(tab = table)不为null,且(n = tab.length)必然不为零,所以if条件不成立,直接执行下一个if语句;
n = (tab = resize()).length;
if ((p = tab[i = (n - 1) & hash]) == null)//该hash中存储的int类型"Lucy"随机生成了在n-1中的一个i值,且由于该hash存储的数据与此前存储"Tom"数据的hash不同,因此该i与此前的i不同,该tab[i]为null,因此p = tab[i = (n - 1) & hash]为null,执行下一个语句;
tab[i] = newNode(hash, key, value, null);//存入"Lucy"数据;
//(中间不执行语句省略)
if(++size > threshold)
resize();
afterNodeInsertion(evict);
return null;
}