较真儿学源码系列-HashSet(逐行源码带你分析作者思路)

        Java版本:8u321。

        HashSet的底层是通过HashMap来实现的,之前我写过对HashMap源码进行分析的文章,感兴趣的话可以查看《较真儿学源码系列-HashMap(逐行源码带你分析作者思路)》。


1 简介

        相比于List接口实现,Set是无序不重复的集合,所以我们可以通过它来实现去重的业务逻辑。而具体到HashSet来说其底层是通过HashMap来实现的,也就是说HashSet的特性是通过HashMap来保证的(相同值会进行覆盖)。key为需要存储的值,而value是一个没有任何意义的占位值。所以在HashSet中,所有key对应的value都为这个空的占位置。我们不需要关注value,只需要关注key就行了。

        但是相比于HashMap,HashSet不提供get方法,这是因为通过key找到的value总是那个虚拟的占位值,没有什么意义;而且对于Set来说插入的顺序并不等于实际存储的顺序(HashMap的散列特性),所以通过索引位找到值的get方法也是不能提供的。


2 构造器

/**
 * HashSet:
 * 无参构造器
 * 可以看到底层就是调用的HashMap的无参构造器
 */
public HashSet() {
    map = new HashMap<>();
}

/**
 * 有参构造器
 * 可以看到底层就是调用的HashMap的有参构造器
 */
public HashSet(int initialCapacity) {
    map = new HashMap<>(initialCapacity);
}

/**
 * 将其他集合转成HashSet
 */
public HashSet(Collection c) {
    //因为HashMap有负载因子的概念,所以取原有集合的长度/0.75+1和16的最大值最为HashMap的初始容量
    map = new HashMap<>(Math.max((int) (c.size() / .75f) + 1, 16));
    //然后将原有集合的数据添加到HashSet中
    addAll(c);
}

/**
 * AbstractCollection:
 * 第25行代码处:
 * 可以看到是通过遍历调用add方法来实现的
 */
public boolean addAll(Collection c) {
    boolean modified = false;
    for (E e : c)
        if (add(e))
            modified = true;
    return modified;
}

3 add方法

/**
 * HashSet:
 */
public boolean add(E e) {
    /*
    这里直接调用的HashMap的put方法来存入值,PRESENT的定义为“private static final Object PRESENT = new Object();”,
    其只是一个占位的虚拟节点,没有其他意义。而对于HashMap的put方法来说,返回值为null代表新插入了一个键值对,而返回值不为null
    代表此时已经有这个键值对了,只是在做值覆盖的操作。所以通过判断HashMap插入结果是否为null,以此来说明HashSet是否插入成功了
     */
    return map.put(e, PRESENT) == null;
}

4 remove方法

/**
 * HashSet:
 */
public boolean remove(Object o) {
    //通过调用HashMap的remove方法来实现的,如果返回值是PRESENT,代表之前有值;否则代表没值
    return map.remove(o) == PRESENT;
}

5 clear方法

/**
 * HashSet:
 */
public void clear() {
    //依然是通过调用HashMap的clear方法来实现的
    map.clear();
}

原创不易,未得准许,请勿转载,翻版必究

你可能感兴趣的:(数据结构与算法,HashSet,HashSet源码分析)