HashSet采用组合+委托而非代理模式,因其内部持HashMap实例并直接委托操作,但HashMap不实现Set接口,无共同接口、无横切逻辑、不可替换,不符合代理模式定义。

HashSet 的底层结构是 HashMap
`HashSet` 的源码非常简洁,核心逻辑几乎全部委托给一个 `HashMap` 实例:
- 它持有一个私有 `HashMap
` 字段(值固定为 `PRESENT` 占位符) - 所有操作如 `add()`、`remove()`、`contains()` 都直接调用该 `HashMap` 对应的方法
- 例如:
public boolean add(E e) { return map.put(e, PRESENT) == null; }
这不是代理模式,而是委托(Delegation)
代理模式要求:
- 代理类和被代理类实现同一接口
- 客户端面向接口编程,能无感知地用代理替换真实对象
- 代理在转发前后可添加横切逻辑(如日志、权限、延迟加载等)
而 `HashSet`:
- 它自己实现了 `Set
` 接口,HashMap 并不实现 Set 接口,二者没有共同接口 - 客户端只跟 `HashSet` 交互,根本不知道 `HashMap` 的存在,也**无法把 `HashSet` 当作 `HashMap` 的代理来替换使用**
- 它的“委托”是纯粹的内部实现复用,没有控制访问、增强行为、权限拦截等代理意图
容易混淆的点:为什么有人觉得像代理?
因为 `HashSet` 把操作“转给”了 `HashMap`,看起来像“替别人干活”。但这只是封装内部实现细节的常规做法,类似:
立即学习“Java免费学习笔记(深入)”;
- `ArrayList` 内部用数组,`LinkedList` 内部用链表 —— 这叫实现选择,不是代理
- `Collections.synchronizedSet()` 返回一个包装类,它才真正符合代理模式:它实现了 `Set`,内部持有一个 `Set`,并在每个方法前后加同步 —— 这才是典型代理
真正的代理模式在 JDK 中的例子
如果你想找 Java 标准库中符合代理模式的实现,可以看:
-
Collections.unmodifiableXXX()系列(如unmodifiableSet()):返回一个代理对象,实现相同接口,拒绝修改操作 -
java.lang.reflect.Proxy动态生成的代理类(如 Spring AOP 底层) - RMI 中的 Stub 类:远程代理,实现与远程对象相同的接口
总之,`HashSet` 是良好封装的典范,但它走的是组合委托路线,不是代理模式。理解这点,能帮你更准确识别设计模式的真实应用场景。


















