HashSet、TreeSet 源码解析-10

HashSet
1.底层实现基于 HashMap,所以迭代时不能保证按照插入顺序,或者其它顺序进行迭代;
2.add、remove、contanins、size 等方法的耗时性能,是不会随着数据量的增加而增加的,这个主要跟 HashMap 底层的数组数据结构有关,不管数据量多大,不考虑 hash 冲突的情况下,时间复杂度都是 O (1);
3.线程不安全的,如果需要安全请自行加锁,或者使用 Collections.synchronizedSet;
4.迭代过程中,如果数据结构被改变,会快速失败的,会抛出 ConcurrentModificationException 异常。
HashSet 的实现是基于 HashMap 的,在 Java 中,要基于基础类进行创新实现,有两种办法:
继承基础类,覆写基础类的方法,比如说继承 HashMap , 覆写其 add 的方法;
组合基础类,通过调用基础类的方法,来复用基础类的能力。
HashSet 使用的就是组合 HashMap,其优点如下:
继承表示父子类是同一个事物,而 Set 和 Map 本来就是想表达两种事物,所以继承不妥,而且 Java 语法限制,子类只能继承一个父类,后续难以扩展。
组合更加灵活,可以任意的组合现有的基础类,并且可以在基础类方法的基础上进行扩展、编排等,而且方法命名可以任意命名,无需和基础类的方法名称保持一致。
组合就是把 HashMap 当作自己的一个局部变量,以下是 HashSet 的组合实现

// 把 HashMap 组合进来,key 是 Hashset 的 key,value 是下面的 PRESENT
private transient HashMap<E,Object> map;
// HashMap 中的 value
private static final Object PRESENT = new Object();

从这两行代码中,我们可以看出两点:
我们在使用 HashSet 时,比如 add 方法,只有一个入参,但组合的 Map 的 add 方法却有 key,value 两个入参,相对应上 Map 的 key 就是我们 add 的入参,value 就是第二行代码中的 PRESENT,此处设计非常巧妙,用一个默认值 PRESENT 来代替 Map 的 Value;
如果 HashSet 是被共享的,当多个线程访问的时候,就会有线程安全问题,因为在后续的所有操作中,并没有加锁。
HashMap 初始化大小值的模版公式:取括号内两者的最大值(期望的值 / 0.75+1,默认值 16)。
TreeSet
TreeSet 大致的结构和 HashSet 相似,底层组合的是 TreeMap,所以继承了 TreeMap key 能够排序的功能,迭代的时候,也可以按照 key 的排序顺序进行迭代,我们主要来看复用 TreeMap 时,复用的两种思路:
2.1 复用 TreeMap 的思路一
场景一: TreeSet 的 add 方法,我们来看下其源码:

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

可以看到,底层直接使用的是 HashMap 的 put 的能力,直接拿来用就好了
我们总结下 TreeSet 组合 TreeMap 实现的两种思路:
TreeSet 直接使用 TreeMap 的某些功能,自己包装成新的 api。
TreeSet 定义自己想要的 api,自己定义接口规范,让 TreeMap 去实现。
HashSet 和 TreeSet 的面试概率比不上 List 和 Map,但只要有机会,并把本文的内容表达出来,绝对是加分项,因为现在 List 和 Map 面试题太多,面试官认为你能答的出来是应该的,但只要你有机会对 HashSet 和 TreeSet 说出本文见解,并且说自己是看源码时领悟到的,绝对肯定是加分项,这些就是你超过面试官预期的惊喜,以下是一些常用的题目:
3.1 TreeSet 有用过么,平时都在什么场景下使用?
答:有木有用过如实回答就好了,我们一般都是在需要把元素进行排序的时候使用 TreeSet,使用时需要我们注意元素最好实现 Comparable 接口,这样方便底层的 TreeMap 根据 key 进行排序。
3.2 追问,如果我想实现根据 key 的新增顺序进行遍历怎么办?
答:要按照 key 的新增顺序进行遍历,首先想到的应该就是 LinkedHashMap,而 LinkedHashSet 正好是基于 LinkedHashMap 实现的,所以我们可以选择使用 LinkedHashSet。
3.3 追问,如果我想对 key 进行去重,有什么好的办法么?
答:我们首先想到的是 TreeSet,TreeSet 底层使用的是 TreeMap,TreeMap 在 put 的时候,如果发现 key 是相同的,会把 value 值进行覆盖,所有不会产生重复的 key ,利用这一特性,使用 TreeSet 正好可以去重。
3.4 说说 TreeSet 和 HashSet 两个 Set 的内部实现结构和原理?
答: HashSet 底层对 HashMap 的能力进行封装,比如说 add 方法,是直接使用 HashMap 的 put 方法,比较简单,但在初始化的时候,我看源码有一些感悟:说一下 HashSet 小结的四小点。
TreeSet 主要是对 TreeMap 底层能力进行封装复用,我发现了两种非常有意思的复用思路,重复 TreeSet 两种复用思路。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

大数据学习爱好者

你的鼓励是我最大的动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值