JDK源码分析之Set类详解——适配器模式的应用
JDK 源码分析之 Set 类详解——适配器模式的应用
编者注:本文为历史博文归档;涉及 JDK、框架与工具链版本请以当前官方文档为准。引用外链图片可能失效,阅读时请注意时效性。
JDK 源码中的 Set 类是开发过程中经常使用的集合类型。本文将对 JDK 源码中 Set 类的构造进行深入分析,帮助开发者在编程中更高效地应用。
Set 类的核心特性与实现原理
在 JDK 源码中,Set 类最常用的特性是不允许包含重复元素。那么,判断元素是否相同是如何实现的呢?我们可以通过下面的类图来直观理解:
观察上述类图,我们可以发现一个经典设计模式的应用,那就是适配器模式(Adapter Pattern)。源码将 Map 接口的对象包装成为了 Set 接口。下面我们通过代码具体分析这一实现机制。
HashSet 源码分析
首先查看 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();可见,HashSet 内部适配了 HashMap。那么它的功能是如何委托给 HashMap 结构的呢?关键在于 add 方法的实现:
public boolean add(E e) {
return map.put(e, PRESENT) == null;
}在 HashMap 中,我们大多数时候关注的是 value,但是在 Set 的实现中,却很好地利用了已有类 HashMap 的特性。它利用了 HashMap 的 key 的唯一性来保证存储在 Set 中的元素的唯一性。
private static final Object PRESENT = new Object();上述 PRESENT 对象是 HashMap 所有 key 对应的 value,它只是一个形式占位符,而我们真正的数据是存储在 key 中的资源。
此外,HashSet 拿到的迭代器也是基于 Map 的 keySet:
public Iterator<E> iterator() {
return map.keySet().iterator();
}即直接返回 Map 的 keySet 的迭代器。
LinkedHashSet 源码分析
同理,我们看看 LinkedHashSet 的实现。它提供了多个构造方法:
public LinkedHashSet(int initialCapacity, float loadFactor) {
super(initialCapacity, loadFactor, true);
}
/**
* Constructs a new, empty linked hash set with the specified initial
* capacity and the default load factor (0.75).
*
* @param initialCapacity the initial capacity of the LinkedHashSet
* @throws IllegalArgumentException if the initial capacity is less
* than zero
*/
public LinkedHashSet(int initialCapacity) {
super(initialCapacity, .75f, true);
}
/**
* Constructs a new, empty linked hash set with the default initial
* capacity (16) and load factor (0.75).
*/
public LinkedHashSet() {
super(16, .75f, true);
}这些构造方法均调用了父类(HashSet)的构造函数。父类中对应的构造函数如下:
HashSet(int initialCapacity, float loadFactor, boolean dummy) {
map = new LinkedHashMap<>(initialCapacity, loadFactor);
}由此可见,LinkedHashSet 内部实例化了 LinkedHashMap,从而保持了元素的插入顺序。
TreeSet 源码分析
同理,我们亦可见到 TreeMap 的实现影子。在 TreeSet 中,底层存储结构为:
private transient NavigableMap<E, Object> m;
// Dummy value to associate with an Object in the backing Map
private static final Object PRESENT = new Object();设计模式总结
综上所述,Set 接口的实现类内部均委托给了 Map 接口的实现类。虽然从某些角度理解,这也可以被视为桥接模式(Bridge Pattern)的一种变形,但从语义意义上分析,我更倾向于认为这是适配器模式的应用:将 Map 适配为 Set 接口供外部使用。
希望这篇对 JDK 源码中 Set 类的分析能对你有所帮助。
说明:本文基于早期 JDK 版本源码分析(参考图片上传时间为 2009 年)。虽然核心实现原理(基于 Map 实现 Set)在后续版本(如 JDK 1.8+)中依然保持兼容,但具体内部类名或方法细节可能随版本迭代有所调整,请以当前官方文档为准。
版权声明:本文为原创文章,版权归 戴老师的博客 所有,转载请联系博主获得授权。
如果对本文有什么问题或疑问都可以在评论区留言,我看到后会尽量解答。
