HashSet源码分析

Posted rouqinglangzi

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了HashSet源码分析相关的知识,希望对你有一定的参考价值。

HashSet底层是HashMap实现的,关于HashMap的分析请移步到HashMap源码分析

属性

//底层使用HashMap来实现
private transient HashMap<E,Object> map;
 
//虚拟的Object对象作为HashMap的value
private static final Object PRESENT = new Object();

构造方法

public HashSet() {
    //底层会初始化一个空的HashMap,并使用默认初始容量为16和加载因子0.75。
        map = new HashMap<>();
    }
    
    public HashSet(Collection<? extends E> c) {
        map = new HashMap<>(Math.max((int) (c.size()/.75f) + 1, 16));
        addAll(c);
    }
 
    public HashSet(int initialCapacity, float loadFactor) {
        map = new HashMap<>(initialCapacity, loadFactor);
    }
 
    public HashSet(int initialCapacity) {
        map = new HashMap<>(initialCapacity);
    }
 
    HashSet(int initialCapacity, float loadFactor, boolean dummy) {
        map = new LinkedHashMap<>(initialCapacity, loadFactor);
    }

初始化HashSet时,可以指定初始化容量和负载因子。因为HashSet是由HashMap来实现,如果不指定,则默认值都与HashMap中的一样,即默认初始化容量为16,默认负载因子为0.75。

add方法

public boolean add(E e) {
    return map.put(e, PRESENT)==null;
}
 
//HashMap的put方法
public V put(K key, V value) {  
    //map为空表时,进行扩充  
    if (table == EMPTY_TABLE) {  
        inflateTable(threshold);  
    }  
    //如果key为null,直接定位到table[0]处,进行处理  
    if (key == null)  
        return putForNullKey(value);  
    //计算key的hash值  
    int hash = hash(key);  
    //根据key的hash,定位key在table中索引  
    int i = indexFor(hash, table.length);  
    //判断key是否存在  
    for (Entry<K,V> e = table[i]; e != null; e = e.next) {  
        Object k;  
        //如果key已存在,则覆盖原value  
        //【判断key相等】:也就是判断两个Object是否相等  
        if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {  
            //暂存旧值
            V oldValue = e.value; 
            //用新值覆盖旧值
            e.value = value;  
            e.recordAccess(this);  
            //返回旧值(方法返回后,可能还要用到旧值)  
            return oldValue;  
        }  
    }//key不存在         
    //修改次数+1  
    modCount++;  
    //添加<k,v>  
    addEntry(hash, key, value, i);  
    return null;  
} 

 

 

 

 

总结
1.HashSet的实现原理?

2.Set中元素是无序的

HashSet set=new HashSet();
set.add("a");
set.add("b");
set.add("c");
set.add("d");
System.out.println(set);//结果:[d,b,c,a]

3.HashSet不允许重复

情景0

//两次添加"a"
HashSet set=new HashSet();
System.out.println(set.add("a"));//结果:true
set.add("b");
set.add("c");
set.add("d");
System.out.println(set.add("a"));//结果:false

情景1

//两次分别添加不同的对象。
HashSet set=new HashSet();
 System.out.println(set.add(new People("张三")));//true
System.out.println(set.add(new People("张三")));//true

情景2

//两次都是添加p1
HashSet set=new HashSet();
        
People p1=new People("张三");
System.out.println(set.add(p1));//true
System.out.println(set.add(p1));//false

情景3

//两次分别添加s1,s2(显然,s1和s2是不同的对象)。
HashSet set=new HashSet();
        
String s1=new String("a");
String s2=new String("a");
 
System.out.println(set.add(s1));//true
System.out.println(set.add(s2));//false

4.HashSet如何保证不重复的?

hashset底层用的hashmap实现,hashset实际上是没有value的,只是用了一个虚拟的value(Object PRESENT),每个key的值都是该value。

当要插入一个存在的对象时,hashmap对相同的key则进行value值覆盖操作,所以相当于用新的<key,PRESENT>覆盖掉旧的<key,PRESENT>。所以表面看起来没有插入新的重复元素,也就保证了不重复。

5.HashSet添加元素的过程

①HashCode
当HashSet在添加元素时,会先调用hashCode()方法,判断即将加入的元素的hashCode是否与集合中的元素有相同的,如果没有,则允许添加该元素。如果有相同的,则继续调用equals()方法,如果equals()方法返回true,则表示对象已经加入过了,不允许再添加了。否则,如果equels方法返回false,则允许添加新元素。
②equals

对于两个对象来说,如果使用equals返回true, 则这两个对象的hashcode一定相同。
对于两个对象来说,如果使用equals返回false,则这两个对象的hashcode不一定不相同(可以相同或者不同)。如果不同,可以提高性能。

对于Object类来说,不同的Object对象的hashcode值是不同的(hashCode值表示对象的地址)

String类的hashCode()方法重写了Object类的hashCode()方法,只要两个String对象的内容相同则认为hashCode相同,所以情景3比较特殊。

以上是关于HashSet源码分析的主要内容,如果未能解决你的问题,请参考以下文章

HashSet源码分析

Hashset源码分析

HashSet源码分析

HashSet 源码分析

JDK源码分析——HashSet

深入源码分析HashSet