真正受限访问需通过自定义iterator控制每次遍历,而非暴露原始集合或不可变包装;其核心是在hasnext()和next()中嵌入权限检查、限流、过滤等逻辑,并覆盖remove()和foreachremaining()以杜绝绕过。

为什么不能直接暴露 private List 字段
因为返回 new ArrayList(privateList) 或 Collections.unmodifiableList() 只能防写,不能防“看到全部”——调用方仍能通过 size()、get(0)、toArray() 等方式窥探全部元素,违背“受限访问”的本意。
真正受限,是让使用者只能按你设计的顺序、条件、次数去取数据,且无法绕过控制逻辑。迭代器模式的核心价值不在封装,而在“可控遍历”。
- 暴露原始集合或不可变包装,等于交出索引权和长度信息
- 返回自定义
Iterator才能拦截每次next(),插入权限检查、懒加载、限流或转换逻辑 - Java 的
Iterable接口只需实现iterator()方法,不强制暴露底层结构
如何写一个带访问控制的 Iterator 实现
关键不是重写整个迭代器,而是把校验和过滤逻辑下沉到 hasNext() 和 next() 里。比如只允许读前 5 条、或仅返回状态为 ACTIVE 的对象。
示例场景:一个管理用户会话的私有 Map<string session></string>,外部只能按创建时间顺序逐个查看未过期的会话,且每次最多取 10 个:
public class SessionIterable implements Iterable<session> {
private final Map<string session> sessions;
public SessionIterable(Map<string session> sessions) {
this.sessions = sessions;
}
@Override
public Iterator<session> iterator() {
return new SessionIterator();
}
private class SessionIterator implements Iterator<session> {
private final Iterator<session> baseIter;
private int count = 0;
SessionIterator() {
// 按创建时间排序后过滤过期项,再转成迭代器
List<session> validSorted = sessions.values().stream()
.filter(s -> !s.isExpired())
.sorted(Comparator.comparing(Session::getCreatedAt))
.collect(Collectors.toList());
this.baseIter = validSorted.iterator();
}
@Override
public boolean hasNext() {
return count
<ul>
<li>不要在构造 <code>SessionIterator</code> 时做耗时操作(如 DB 查询),应提前准备好可迭代源</li>
<li>
<code>hasNext()</code> 必须幂等;<code>next()</code> 要负责推进状态,否则多次调用会重复返回同一元素</li>
<li>如果底层数据可能并发修改,需考虑加锁或用线程安全容器,否则 <code>ConcurrentModificationException</code> 仍会发生</li>
</ul>
<h3>
<code>Iterator</code> 和 <code>Stream</code> 在受限访问中的取舍</h3>
<p>有人倾向用 <code>Stream</code> 链式过滤再 <code>limit()</code>,但这是错的:它只限制输出数量,不阻止调用方反复调用 <code>stream()</code> 获取新流,也无法防止 <code>count()</code> 或 <code>toList()</code> 泄露总量。</p>
<ul>
<li>
<code>Iterator</code> 是单次、有状态的;<code>Stream</code> 是无状态、可重复构建的——后者天然不适合“受限”语义</li>
<li>若必须用 <code>Stream</code>,得配合自定义 <code>Spliterator</code>,但复杂度陡增,且多数 JDK 版本不支持对外隐藏 <code>estimateSize()</code>
</li>
<li>简单场景下,<code>Iterator</code> 更轻量、意图更清晰;复杂过滤+分页建议用专门的 <code>PageableIterator</code> 类,而非硬塞进标准接口</li>
</ul>
<h3>容易被忽略的边界:<code>remove()</code> 和 <code>forEachRemaining()</code>
</h3>
<p>很多人只重写 <code>hasNext()</code> 和 <code>next()</code>,却忘了 <code>Iterator</code> 接口还包含 <code>remove()</code> 和 <code>forEachRemaining()</code>。前者默认抛 <code>UnsupportedOperationException</code>,但如果你没显式覆盖,别人反射调用或通过 <code>Collection.forEach()</code> 触发,可能意外绕过你的控制逻辑。</p>
<ul>
<li>明确禁止删除?重写 <code>remove()</code> 并抛 <code>UnsupportedOperationException</code>
</li>
<li>想禁用批量消费?重写 <code>forEachRemaining()</code>,内部调用受控的 <code>next()</code>,而不是直接委托给底层迭代器</li>
<li>测试时别只跑 <code>while (it.hasNext()) it.next()</code>,务必试 <code>it.forEachRemaining(...)</code> 和 <code>it.remove()</code> 是否按预期失败</li>
</ul>
<p>真正的受限,是每条路径都收口。漏掉一个方法,就等于留了一扇没锁的窗。</p></session></session></session></session></string></string></session>










