sublist截取前n个元素最轻量但返回原列表视图,修改会影响原列表;需独立操作时应转为new arraylist或用stream.limit(n).collect(...)。

用 subList 截取前 N 个元素最直接,但要注意它返回的是原 List 的视图
如果你只是想快速拿到前 N 个元素,subList 是最轻量的选择。它不复制数据,只是返回一个指向原 List 的“窗口”。
- 调用
list.subList(0, Math.min(n, list.size()))可安全避免IndexOutOfBoundsException - 修改子列表会影响原列表(比如调用
clear()或set()),这点常被忽略 - 如果后续要独立操作、序列化或传给不信任的代码,得显式转成新列表:
new ArrayList(list.subList(...)) -
subList在LinkedList上随机访问性能差(O(N)),别在大链表上反复调用get(i)
用 Stream.limit(n) 更函数式,但会触发完整遍历和装箱开销
流式写法适合已处在 Stream 处理链中,或者需要配合 filter/map 等操作的场景;单独只为截断,反而多了一层封装。
-
list.stream().limit(n).collect(Collectors.toList())总是返回新ArrayList,无副作用 - 对
ArrayList,limit内部仍需从头迭代,不会跳过——哪怕你只想要前 3 个,它也得走过索引 0~2 - 原始类型数组或
IntStream等场景下,limit不引发装箱;但List<integer></integer>走stream()就必然有自动装箱/拆箱成本 - 如果
n > list.size(),limit安全无异常,而subList(0, n)会炸
分页场景下别直接用 limit 或 subList 做 offset + limit
真正在做分页(比如第 3 页、每页 10 条),光靠 limit 或 subList 很难写出健壮逻辑——它们都不支持跳过前 M 项再取 N 项的原子操作。
-
subList(fromIndex, toIndex)需手动算from = page * size,容易越界,必须加Math.min(toIndex, list.size()) -
Stream.skip(m).limit(n)看似简洁,但skip会强制消费前 m 个元素,无法短路;大数据量时纯属浪费 - 数据库分页应交由 SQL 的
LIMIT/OFFSET或FETCH NEXT,Java 层只负责小结果集的展示截断 - 若必须内存分页,建议封装工具方法,统一处理边界,并明确文档是否返回视图还是副本
选哪个?看你的「所有权」和「后续用途」
核心判断不是性能或语法酷不酷,而是:你拿到这 N 个元素后,要不要改它?会不会传给别的模块?有没有并发风险?
立即学习“Java免费学习笔记(深入)”;
- 只读展示、生命周期短、确定不会被改 →
subList最省事 - 要塞进 JSON、存缓存、传给第三方 SDK、或担心别人误改原列表 → 用
Stream.limit(n).collect(...)或new ArrayList(...) - 处理的是
LinkedList且 N 很小 →subList仍可,但别反复get中间位置;否则考虑先转ArrayList - 已在用 Stream 流水线 → 别为了截断额外切回集合操作,保持风格一致
最容易被忽略的一点:很多人以为 subList 是“安全复制”,结果线上出现诡异的数据同步问题——其实只是忘了它是视图。










