集合篇:Set—HashSet源码解析


Java中的HashSet和TreeSet两个类都是在Map的基础上组装而来的。侧重点也在于Set如何利用Map现有的功能实现自身的设计。

1.HashSet整体架构

HashSet是对HashMap进行组装后的产物,源码文件中对该类的描述具体有如下四点,

  1. 底层实现基于HashMap,所以迭代过程中不能维护插入顺序。
  2. add、remove、contains、size等方法的耗能不会随着数据的增加而增加。主要与HashMap底层数组结构相关,不管数据量多大,不考虑hash冲突时,时间复杂度都是O(1)。
  3. 作为共享变量时,线程不安全,如果需要保证线程安全,则序通过Collections.synchronizedSet方法将其转换为线程安全的集合。
  4. 迭代过程中如果数据结构发生变化,会抛出ConcurrentModificationException异常。

上面这四点中,2、3、4在List和Map中也都提及,故这三条是List、Map和Set的共同特点。

2.HashSet组合HashMap的方法

Java中基于基础类进行创新的方式主要有两种,

  • 继承基础类,复写基础类的方法,比如LinkedHashMap对HashMap访问元素过程在调用get方法时的afterNodeAccess方法进行复写,实现LRU。
  • 组合基础类,通过调用基础类的方法,来复用基础类的能力。

HashSet选取的就是组合HashMap的方法,这种方式的好处是,

  1. 继承表示父类和子类是同一个事物,而Set和Map本身想表达的是两个概念,所以使用继承的方式不妥当。且因为Java语法的限制,子类只能有一个父类,后续难以拓展。
  2. 组合的方式更加灵活,可以任意组合现有的基础类,并在基础类的方法的基础上进行拓展、编排等。而且方法名称可以任意命名,无需与基础类方法名称保持一致。

在平时的开发中,碰到类似问题应尽量多使用组合,少用继承。

组合HashMap就是将HashMap作为自身的一个局部变量,从HashSet的构造方法中就可以看出,

private transient HashMap<E,Object> map;

// Dummy value to associate with an Object in the backing Map
private static final Object PRESENT = new Object();

// 无参数初始化
public HashSet() {
    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);
}

	......

所有的初始化方式中都会初始化一个HashMap对象。同时HashSet类始终存在一个Object对象,该对象在调用add方法时会用到,

public boolean add(E e) {
    return map.put(e, PRESENT)==null;
}

HashMap中的key是唯一的,HashSet借用了HashMap的这一属性。对于value,设置为相同的值即可。

总结

HashSet内部本质就是HashMap,只是对HashMap做了一层外包。利用HashMap已有的功能完成自己的需求。没有使用继承的方式,使得其方法可以自己定义,无需与HashMap保持一致。

如果想依据key的插入顺序进行遍历,使用LinkedHashSet类即可。LinkedHashSet是基于LinkedHashMap实现的。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值