介绍
1、无序、无索引
2、不允许重复,因此最多只能有一个null
HashSet
基本介绍
1、实现Set接口
2、本质上是HashMap:
public HashSet(){
map=new HashMap<>();
}
3、可存null值,但最多只能有一个
4、无序,取决于hash之后,再确定索引的结果
5、不允许重复
add()底层
1、第一次添加时,table数组扩容到16,临界值(threshold)为16加载因子(loadFactor)0.75=12
2、如果table数组使用到了临界值12(>=),则会扩容到162=32,新的临界值为32*0.75=24,以此类推
3、HashMap中,(JDK8)如果一条链表的元素个数超过TREEIFY_THRSHOLD(默认为8),并且table的大小>=MIN_TREEIFY_CAPACITY(默认为64)就会进行树化(红黑树)
源码
public HashSet(){
map=new HashMap();
}
public boolean add(E e){
return map.put(e,PRESENT)=null;
// 静态占位符
// (static) PRESENT =new Object();
}
//hash值并不是直接由hashCode()求出
public V put(K key,V value){
return putVal(hash(key),key,value,false,true);
}
//hash(key)
static final int hash(Object key){
int h;
return (key==null)?0:(h=key.hashCode())^(h>>>16);
//按位异或再右移16位
}
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=tabl.length)==0)//数组为null或长度为0
n=(tab=resize()).length;//第一次扩容到16个空间
/*
(1)根据key,得到hash。计算该key应该存放到table表的哪个索引位置,
并把这个对象赋值给p
(2)判断p是否为null
(2.1)如果为null,则表示无元素,就创建一个Node(key,value)
这里的value就是PRESENT
(2.2)放在该位置tab[i]=newNode(hash,key,value,null)
*/
if(cp=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;
/*
如果当前索引位置对应的链表的第一个元素和准备添加的key都hash值一样,
且引用相同或equals返回true,则不能加入
*/
else if(p instanceof TreeNode)
e=((TreeNode<K,V>)p).putTreeVal(this,tab,hash,key,value);
//判断p是否为一颗红黑树,如果是则调用putTreeVal添加
else{
//遍历比较若有相同则不能加入
for(int binCount=0; ;++binCount){
if((e=p.next)=null){
p.next=newNode(hash,key,value,null);
if(binCount>=TREEIFY_THRESHOLD-1)
treeifyBin(tab,hash);
/*
添加元素之后,若链表长度达到8个,
则对此链表树化(若表<64,则先扩容resize())
*/
break
}
if(e.hash==hash)
break;
p=e;
}
}
}
}
这里的源码解读我是学习韩老师的视频讲解: 韩顺平老师的讲解.
LinkedHashSet
基本介绍
1、HashSet的子类
2、底层为LinkedHashMap,维护了一个数组加双向链表
3、根据hashCode值来决定元素的存储位置,同时用链表维护元素的次序,这使元素看起来是以插入顺序保存的
4、不允许重复元素
底层
1、第一次添加时,直接将数组table扩容到16,存放的节点类型是LinkedHashMap $ Entry
2、数组为HashMap $ Node[]
//继承关系是在内部类中完成的
源码
static class Entry<K,V> extends HashMap.Node<K,V>{
Entry<K,V> before,after;
Entry(int hash,K key,V value,Node<K,V> next){
super(hash,key,value,next);
}
}