HashSet、LinkedHashSet 两个类是在 HashMap / LinkedHashMap 的基础上组装起来的类,因此本文侧重:java
- 为何要组合 HashMap / LinkedHashMap?
- 如何组合 HashMap / LinkedHashMap?
1、什么是 HashSet ?
- 线程不安全
- 底层实现基于 HashMap
- 迭代过程当中,若是数据被修改,会产生快速失败。
1. 成员变量
public class HashSet<E> extends AbstractSet<E> implements Set<E>, Cloneable, java.io.Serializable { ... // 键值Map private transient HashMap<E,Object> map; // 用做全部键对应的值,键所对应的值都相等 private static final Object PRESENT = new Object(); ... }
Q1:为何要组合 HashMap 而不继承 HashMap?安全
- 继承表示父子类是同一类事物,而 Set 和 Map 原本就是想表达两类事物,因此用继承欠妥,并且 java 只支持单继承,可扩展性不强。
- 组合更加灵活,能够任意的组合现有的基础类,而且能够在基础类方法的基础上进行扩展。
2. 构造方法
// 调用的是HashMap的构造方法 public HashSet() { map = new HashMap<>(); } public HashSet(int initialCapacity, float loadFactor) { map = new HashMap<>(initialCapacity, loadFactor); } public HashSet(int initialCapacity) { map = new HashMap<>(initialCapacity); } public HashSet(Collection<? extends E> c) { map = new HashMap<>(Math.max((int) (c.size()/.75f) + 1, 16)); addAll(c); }
Math.max ((int) (c.size ()/.75f) + 1, 16)
,对 HashMap 的容量进行了计算:线程
- HashMap 的阈值计算:Map 的容量 * 0.75f
- 若是给定 HashMap 初始容量小于 16 ,就按照 HashMap 默 认的 16 初始化
Q:往 HashMap 里拷贝大集合时,如何给 HashMap 初始化大小?code
- A:借鉴上述初始化思路:
max(指望值 / 0.75 + 1,默认值 16)
2、什么是 LinkedHashSet?
- LinkedHashSet 是具备可预知迭代顺序的 Set 接口的哈希表和连接列表实现。
- LinkedHashSet 内部维护着一条双向列表,此连接列表定义了迭代顺序,可为插入顺序或是访问顺序。
- LinkedHashSet 里面有一个 LinkedHashMap (适配器模式),对 LinkedHashSet 的方法调用都会转换成合适的 LinkedHashMap 方法。
public class LinkedHashSet<E> extends HashSet<E> implements Set<E>, Cloneable, java.io.Serializable { ... public LinkedHashSet(int initialCapacity, float loadFactor) { map = new LinkedHashMap<>(initialCapacity, loadFactor); } ... public boolean add(E e) { return map.put(e, PRESENT)==null; } ... }