集合浅谈之HashSet篇

简介

  • HashSet允许存在null值,不允许set中元素重复,也就是只允许存在一个null值
  • HashSet源码中是通过HashMap实现的,与键进行关联的值为一个虚拟值,值没有什么意义故其元素存放规则同HashMap相同,是按照hash值进行存放故其中的元素是无序的
    集合浅谈之HashSet篇

源码分析

public class HashSet<E> extends AbstractSet<E> implements Set<E>, Cloneable, java.io.Serializable
{
//HashSet中存在一个HashMap引用类型对象,键为需要存储到HashSet中的值,值为没有实际意义的Object对象,其实HashSet的本质还是HashMap
 private transient HashMap<E,Object> map;
 //与键进行关联的虚拟值,每个键的值都为PRESENT
 private static final Object PRESENT = new Object();
 
 /**
 构造方法
 通过构造方法我们可以看到调用HashMap中的构造方法,构造的HashMap对象
 */
 //默认无参构造方法
  public HashSet() {
        map = new HashMap<>();
    }
    //通过另一个集合构造HashSet
     public HashSet(Collection<? extends E> c) {
     //构造HashMap对象,初始容量使用HashMap的默认容量与集合容量/构造因子得到容量中的较大值
        map = new HashMap<>(Math.max((int) (c.size()/.75f) + 1, 16));
        addAll(c);
    }
    //HashMap中指定初始容量与加载因子的构造函数
     public HashSet(int initialCapacity, float loadFactor) {
        map = new HashMap<>(initialCapacity, loadFactor);
    }
    //hashMap中指定初始容量的构造方法
    public HashSet(int initialCapacity) {
        map = new HashMap<>(initialCapacity);
    }
    //用于构造一个LinkedHashMap,该构造方法只能由LinkedHashSet使用
    HashSet(int initialCapacity, float loadFactor, boolean dummy) {
        map = new LinkedHashMap<>(initialCapacity, loadFactor);
    }
    //add()方法,add方法最终调用了HashMap中的putVal()方法,键值对中值为虚拟值PRESENT
    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);
    }
}

问1:HashSet是如何做到其中元素不重复的?

如前文所述,HashMap把需要存储的值放置于HashMap的key位置,HashMap的key是不能重复的(至于HashMap中如何保证key的不重复,在HashMap篇中有说到),故HashSet中不允许重复元素

问2:HashSet中为什么没有得到元素的方法(get系列)?

  • HashSet是根据key的hash值去存储元素,因此无法根据索引去访问元素
  • HashSet不同于HashMap可以通过键去访问值,HashSet直接把值存储在键中,因此无法通过键去得到值
  • HashSet只能通过迭代器去访问其中的元素