Stream.skip()是最直接跳过前N个元素的方式,返回丢弃前n个元素的新流,惰性执行、不修改原集合;subList()适用于随机访问列表的零拷贝切片;手动Iterator易出错,应避免。

Stream.skip() 是最直接的跳过前N个元素方式
Java 8+ 的 Stream.skip(long n) 就是为此设计的:它返回一个丢弃前 n 个元素的新流,后续操作只作用于剩余部分。它不修改原集合,也不触发计算,属于惰性操作。
常见错误现象:skip() 对空流或 n 超出流长度时不会报错,而是静默返回空流——容易误以为“没生效”,其实是逻辑正确但结果为空。
-
n必须 ≥ 0,否则抛IllegalArgumentException - 对无限流(如
Stream.iterate)也适用,但要注意下游必须有终止操作,否则会卡住 - 性能上,
skip(n)需要遍历前n个元素(哪怕不处理),所以对超大列表 + 大n值时延迟明显 - 示例:
list.stream().skip(3).collect(Collectors.toList())→ 跳过前3个,取其余
用 List.subList() 更快,但只适用于随机访问列表
subList(int fromIndex, int toIndex) 是零拷贝、O(1) 时间的切片操作,前提是底层 List 支持快速随机访问(如 ArrayList)。它返回的是原列表的视图,修改会影响原列表。
使用场景:已知是 ArrayList 或 Arrays.asList() 结果,且不需要流式处理,只要“从第N个开始的子列表”。
立即学习“Java免费学习笔记(深入)”;
- 如果
fromIndex > list.size(),抛IndexOutOfBoundsException(不是静默失败) -
LinkedList虽然也实现了subList,但内部会遍历到fromIndex,性能退化为 O(n),不如直接用stream().skip() - 注意:返回的子列表不是独立副本,
add()/remove()可能抛UnsupportedOperationException(取决于原始实现) - 示例:
list.subList(3, list.size())→ 等效于跳过前3个,但不创建新元素对象
Iterator 手动 skip 容易出错,慎用
手动调用 iterator().next() N 次看似灵活,实际极易越界或忽略边界条件。它没有内置防护,且破坏了流式链式调用习惯。
常见错误现象:循环中未检查 hasNext() 就调 next() → 抛 NoSuchElementException;或者在 for-each 循环里混用 iterator,导致 ConcurrentModificationException。
- 仅在必须复用已有
Iterator实例(如自定义遍历器)时才考虑 - 必须配对使用
hasNext(),且n超限时需主动 break 或 throw - 无法像
skip()那样与 filter/map 等组合,代码可读性差 - 示例(不推荐但可理解):
Iterator<String> it = list.iterator();<br>for (int i = 0; i < n && it.hasNext(); i++) it.next();<br>// 此时 it 指向第 n+1 个元素
skip() 和 subList() 的兼容性与类型擦除陷阱
二者返回类型不同:skip() 返回 Stream<T>,subList() 返回 List<T>。混用时要注意泛型擦除带来的隐式转换问题,尤其在方法返回值或 lambda 参数中。
容易被忽略的地方:当 List<? extends Number> 这类通配符类型存在时,subList() 仍可工作,但 stream().skip().collect() 可能因类型推导失败而编译报错,需显式指定泛型或用 toList()(Java 16+)。
-
skip()后若接findFirst(),返回Optional<T>;subList()后取get(0)则可能抛异常,语义不同 - Android 开发注意:旧版 API Level(如24以下)不支持
Stream,只能用subList或第三方库(如 Guava) - 多线程下,
subList视图共享原列表状态,而skip()流是无状态的,更安全










