arraylist的sublist不能直接用于分页,因其返回的是原列表的动态视图而非独立副本,修改原列表会导致concurrentmodificationexception;正确做法是用new arraylist(list.sublist(from, to))手动复制子区间,并做好边界、空值和索引检查。

Java 中 ArrayList 的 subList 方法**不能直接用于分页查询逻辑**,它只返回原列表的视图(view),不是独立副本,且在原列表结构变化时会抛出 ConcurrentModificationException。真正安全、可控的分页应手动截取元素或使用更合适的工具。
subList 的本质是视图,不是新列表
subList(fromIndex, toIndex) 返回的是原 ArrayList 的一个动态代理视图,底层仍指向同一数组。这意味着:
- 修改
subList会影响原列表(比如调用add()或remove()); - 若原列表在获取
subList后被结构性修改(如add()、clear()),再访问subList会立即抛异常; - 它不支持
add()、remove()等改变大小的操作(部分操作会抛UnsupportedOperationException)。
分页查询的正确做法:手动复制子区间
要实现稳定、可复用的分页(例如第 page 页,每页 size 条),推荐用 new ArrayList(list.subList(...)) 构造新列表:
public static <t> List<t> paginate(List<t> data, int page, int size) {
if (data == null || data.isEmpty() || page = data.size()) {
return Collections.emptyList();
}
return new ArrayList(data.subList(from, to));
}
</t></t></t>这样得到的是独立、安全、可随意操作的子列表,不会影响原数据,也规避了并发修改风险。
你接手一个需求时,是不是经常这样:"改一个小字段 → 结果把不相关的代码也改了"、"需求说得很模糊 → 做着做着发现跑偏了"、"数据库加个字段 → 上线才发现漏了数据兼容"、"做了个新功能 → 一个月后自己都忘了当时怎么设计的"、"写到一半被中断 → 再回来不知道从哪继续";这个 Skill 就是专门解决这些问题的。 它会自动探测你的项目环境(JDK、Spring Boot、ORM、数据库、前端框架),记在
注意边界处理和空指针
实际使用中容易忽略几个关键细节:
-
索引越界检查:
subList(from, to)要求0 ≤ from ≤ to ≤ list.size(),否则抛IndexOutOfBoundsException; -
空列表/无效页码:
page=0、size=0、或from ≥ size()都应返回空列表,而非硬抛异常; -
null 安全:传入的
data可能为null,需提前判空; -
性能考虑:对超大列表频繁分页时,
subList + new ArrayList会产生额外内存拷贝,如纯读取可考虑封装不可变视图(但需自行保证线程安全)。
替代方案:用 Guava 或 Apache Commons
如果项目已引入第三方库,可简化代码:
-
Guava:
Lists.newArrayList(data).subList(from, to)(仍是拷贝)或Iterables.skip(Iterables.limit(data, to), from)(惰性、无拷贝,适合只遍历场景); -
Apache Commons Collections:
CollectionUtils.select(data, predicate)更适合条件过滤分页,非简单索引分页。
但多数情况下,手写带边界检查的 new ArrayList(list.subList(...)) 最清晰、无依赖、易维护。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










