HashSet/HashMap简介
- hashset是一个单列集合,但是做到了元素的无序和唯一,其实底层原理为hashmap,将key-value中的value替换成一个静态常量,key为添加入集合的元素。
- HashSet存放数据的结构和HashMap一样,为 数组+链表+红黑树 。下图中还未树化,然后Entry就是Node,只是jdk8以后hashmap中Entry改叫Node了哈哈哈哈。。
- HashMap是一个双列集合,做到了元素的无序和唯一
-
HashSet add()方法debug HashMap putVal()方法debug
-
add()方法
map字段 以及hashset无参构造方法
private transient HashMap<E,Object> map;
public HashSet() {
map = new HashMap<>();
}
hashset类中的add方法
public boolean add(E e) {
return map.put(e, PRESENT)==null; //return true表示添加成功
}
//PRESENT为hashset中的static成员常量,实质指向堆内存中一块空间
private static final Object PRESENT = new Object();
map字段指向hashmap实例,调用hashmap的put方法
public V put(K key, V value) {
return putVal(hash(key), key, value, false, true);
}
讲到这里就不得不先引入Object超类中的hashCode方法了,hashCode() 由c++实现,拿到对象的地址(对于HashSet中的元素来说就是key的地址),通过一定的算法解析出hashcode码。值得注意的是hashcode码为int类型,也就是说hashcode支持 -2^32~2^32-1 量级的整型数据范围,共4个字节,32位。
@IntrinsicCandidate
public native int hashCode();
然后再回看putVal中的参数列表第一个形参hash(key),hash()方法是hashmap类中的一个方法,目的是为了解决直接通过hashcode得到数组索引位易出现高哈希冲突频率的,先上方法代码:
static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
可以看到,hash()方法在传入的key有实际指向后return一个h = key.hashCode()) ^ (h >>> 16)
其本质就是由该表达式替换了原先的hashCode方法直接得到的hashcode码,那么为啥要这样呢,可以移步我的另一个回答,上链接:
(h = key.hashCode()) ^ (h >>> 16)_要转码成功呀的博客-CSDN博客
-
putVal()方法
继续,解答完hash(key)后,我们就来到了最主要的添加元素方法主体,putVal方法体:
onlyIfAbsent – if true, don't change existing value
evict – if false, the table is in creation mode.
final V putVal(int hash, K key, V value, boolean onlyIfAbsent,boolean evict)
putVal方法的代码较多,我把它分成三部分来说
-
第一部分:对hashset初始化,扩容机制判断,元素放置索引位的计算
final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
boolean evict) {
//定义了辅助变量 空的tab数组 索引节点p 数组扩容后的长度n 索引编号i
Node<K,V>[] tab; Node<K,V> p; int n, i;
//table: transient Node<K,V>[] table;//成员字段table
//如果tab为空或者tab长度为0就进行第一次扩容
if ((tab = table) == null || (n = tab.length) == 0)
n = (tab = resize()).length; //n=16 tab=table=new Node<K,V>[16]
//根据传入的key,得到hash 去计算要存放在tab中的索引,判断索引位置是否为空,
//为空则添加new Node<K,V>元素
if ((p = tab[i = (n - 1) & hash]) == null)
tab[i] = newNode(hash, key, value, null);
怕有人看不懂newNode怎么蹦出来的,上源代码:
Node<K,V> newNode(int hash, K key, V value, Node<K,V> next) {
return new Node<>(hash, key, value, next);
}
就是一个Node对象而已哈哈哈哈。。。。
这里有必要说明一下,table数组是hashmap中的一个Node<K,V&g