探索HashSet底层实现

前言

HashSet的底层实现依赖于HashMap,所以它的数据结构也是数组 + 链表 + 红黑树,而对于它的类注释也没什么好总结的,探索HashSet底层实现是基于JDK1.8。仔细一想,HashSet存在的意义是什么?有时候需要添加元素时,也就是只有单个对象,并没有所谓的键值对,或许还有些用处,可这ArrayList也能做到啊!可是相比之下,HashSet由于有HashMap撑腰,它的性能要高于ArrayList,所以我认为HashSet是List和Map独有的特性结合后的产物。

数据结构


    //可序列化、可克隆
    public class HashSet<E> extends AbstractSet<E> implements Set<E>, Cloneable, java.io.Serializable {

        //直接使用了HashMap来存储它的元素
        private transient HashMap<E,Object> map;

        //既然用了HashMap就要考虑值应该存什么,就是它了,不管新增的元素是什么,它都作为值
        private static final Object PRESENT = new Object();
    }


构造函数


    /**
     * 初始化
     */
    public HashSet() {
        map = new HashMap<>();
    }

    /**
     * 指定集合来初始化
     * @param c 指定集合
     */
    public HashSet(Collection<? extends E> c) {
        map = new HashMap<>(Math.max((int) (c.size()/.75f) + 1, 16));
        addAll(c);
    }

    /**
     * 指定初始容量与加载因子来初始化
     * @param initialCapacity 指定初始容量
     * @param loadFactor 指定加载因子
     */
    public HashSet(int initialCapacity, float loadFactor) {
        map = new HashMap<>(initialCapacity, loadFactor);
    }

    /**
     * 指定初始容量来初始化
     * @param initialCapacity 指定初始容量
     */
    public HashSet(int initialCapacity) {
        map = new HashMap<>(initialCapacity);
    }

    /**
     * 指定初始容量与加载因子来初始化
     * 对比上面这里构造了LinkedHashMap,说明它是有序的
     * @param initialCapacity 指定初始容量
     * @param loadFactor 指定加载因子
     * @param dummy 无实际意义,为了与上面的构造函数区分开来
     */
    HashSet(int initialCapacity, float loadFactor, boolean dummy) {
        map = new LinkedHashMap<>(initialCapacity, loadFactor);
    }

简单方法


    /**
     * 获取迭代器
     * @return 迭代器
     */
    public Iterator<E> iterator() {
        return map.keySet().iterator();
    }

    /**
     * 获取元素个数
     * @return 元素个数
     */
    public int size() {
        return map.size();
    }

    /**
     * HashSet是否为空
     * @return 是否为空
     */
    public boolean isEmpty() {
        return map.isEmpty();
    }

    /**
     * HashSet是否包含指定元素
     * @param o 指定元素
     * @return 是否包含指定元素
     */
    public boolean contains(Object o) {
        return map.containsKey(o);
    }

    /**
     * 新增元素
     * @param e 指定元素
     * @return 是否新增成功
     */
    public boolean add(E e) {
        return map.put(e, PRESENT)==null;
    }

    /**
     * 移除指定元素
     * @param o 指定元素
     * @return 是否移除成功
     */
    public boolean remove(Object o) {
        return map.remove(o)==PRESENT;
    }

    /**
     * 清空
     */
    public void clear() {
        map.clear();
    }

    /**
     * 浅克隆
     */
    public Object clone() {
        try {
            HashSet<E> newSet = (HashSet<E>) super.clone();
            newSet.map = (HashMap<E, Object>) map.clone();
            return newSet;
        } catch (CloneNotSupportedException e) {
            throw new InternalError(e);
        }
    }


总结

  • HashSet无序、不可重复、非线程安全。

  • HashSet允许存放空元素。

  • HashSet底层基于HashMap。

  • 两个相等的对象,即hashCode与equals都相等的情况,HashMap底层只是进行值替换,并未理睬键,所以就呈现了在往HashSet添加相等的对象时,只添加了第一个对象,第二个对象并未添加。

重点关注

基于HashMap

posted @ 2020-12-21 21:56  zliawk  阅读(306)  评论(0)    收藏  举报