基于jdk1.8的HashSet的源码分析

前言:HashSet也是我们开发中常用的数据结构之一,它的特点是:无序(存储和读取的顺序有可能不一样)、不重复(要求元素唯一)、可以有null值(只能有一个)、没有索引等。其实他的底层是基于HashMap来实现的,所以在看这篇文章之前可以去翻看一下我的上一篇博客 基于jdk1.8的HashMap的源码分析。本文还是一样,会从成员属性、构造方法、常用方法来进行分析,以最简单的方式,来读懂最难的代码。

一:HashSet的具体实现

1.1:HashSet的成员属性
 //使用HashMap用来存储元素
 private transient HashMap<E,Object> map;
 //因为HashSet只用key存储数据,所以这个变量用来向map中存储数据时代替value
 private static final Object PRESENT = new Object();
1.2:HashSet的构造方法
//方法一:通过实例化HashMap来实现
public HashSet() {
        map = new HashMap<>();
    }
//方法二:通过传入集合来实现
 public HashSet(Collection<? extends E> c) {
   			//通过集合大小和HashMap默认的加载因子来初始化容量,后面+1是为了向上取到整数
        map = new HashMap<>(Math.max((int) (c.size()/.75f) + 1, 16));
        addAll(c);
    }
  //添加元素
  public boolean addAll(Collection<? extends E> c) {
        boolean modified = false;
        for (E e : c)
            if (add(e))
                modified = true;
        return modified;
    }
//方法三:通过指定容量大小和加载因子初始化HashMap来实现
public HashSet(int initialCapacity, float loadFactor) {
        map = new HashMap<>(initialCapacity, loadFactor);
    }
//方法四:指定容量大小初始化HashMap来实现
public HashSet(int initialCapacity) {
        map = new HashMap<>(initialCapacity);
    }
    
    
1.3:HashSet的常用方法
1.3.1:add(E e) 方法
//可以看出底层也是通过HashMap的put方法来实现的
//HashMap的put方法返回的是要么是旧值要么是null
//PRESENT初始化是null并且不会改变,所以这个值一直是null,因此put方法返回的就一直是null
public boolean add(E e) {
        return map.put(e, PRESENT)==null;
    }
1.3.2:remove(object o) 和 clear()方法
//可以看出底层也是通过HashMap的remove方法来实现的
//map的remove方法的返回的是被删除键对应的值,存储的时候是null
  public boolean remove(Object o) {
    return map.remove(o)==PRESENT;
  }
 public void clear() {
        map.clear();
    }
1.3.3:contains(Object o)方法
//可以看出底层也是通过HashMap的containsKey方法来实现的
public boolean contains(Object o) {
        return map.containsKey(o);
    }
1.3.4:isEmpty()方法
//可以看出底层也是通过HashMap的isEmpty方法来实现的
public boolean isEmpty() {
        return map.isEmpty();
    }
1.3.5:clone()方法
/**
* 可以看出使用的是Object的clone()方法来实现的,得到Object对象后强转成HashSet<E>
* 接着通过调用的HashMap的clone()方法把它的底层实现HashMap也克隆一份,并赋值给newSet返回
*/
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的常见问题及总结

2.1:为什么使用HashSet?

解析:和LinkedList和ArrayList的查询速度相比,HashSet更快。通过上面分析我们知道HashSet是基于HashMap实现的,并且HashSet只存储key,value是null,当我们去获取数据的时候,就只用根据散列值计算数组下标,从而获取值,速度很快。而LinkedList是基于链表的形式,查找需要逐级遍历,效率低。ArrayList如果不知道元素的的具体位置,还是要全部遍历直到查到结果,效率一样低。

2.2:总结
  • HashSet是基于HashMap实现的,不能有重复的元素;
  • HashSet存储数据是无序的,存储和读取的顺序有可能不一样;
  • HashSet可以插入null,但只能有一个;
  • HashSet不是线程安全的;
  • HashSet和LinkedList和ArrayList的查询速度相比,HashSet更快;
    在这里插入图片描述
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值