Collections.reverse()仅支持可修改的List类型,原地反转且不返回新列表;传入null会抛NullPointerException,非List类型或不可变集合会报错,多线程下需额外同步。

直接调用 Collections.reverse() 即可反转 List,但仅支持 List 类型
Collections.reverse() 是最常用、最轻量的反转方式,但它只接受 List 接口及其实现类(如 ArrayList、LinkedList),对 Set、Map 或只读集合会抛出 UnsupportedOperationException 或编译失败。
常见错误现象:传入 Arrays.asList() 返回的“伪可变”列表后调用 add() 或 remove() 报错 —— 但 reverse() 本身是安全的,因为它只改顺序,不增删元素。
- 必须确保传入的是可修改的
List实例;若来源是Arrays.asList(new String[]{"a","b"}),它返回的是固定大小列表,reverse()可用,但后续若尝试add()就会失败 - 不支持
Set:想“反转 Set”,本质无序,需先转成ArrayList再反转 - 不支持
Map:需提取keySet()或entrySet()转为List后操作
反转前先确认 List 是否为空或为 null,避免 NullPointerException
虽然 Collections.reverse() 自身对空 List 安全(无副作用),但若传入 null,会直接抛出 NullPointerException。生产代码中常忽略这一层防御。
使用场景:从 DAO 层查出的 List 可能为 null(尤其 MyBatis 某些配置下未设默认返回空集合)。
立即学习“Java免费学习笔记(深入)”;
- 推荐写法:
if (list != null) { Collections.reverse(list); } - 更稳妥可用
Objects.requireNonNullElse(list, Collections.emptyList()),但注意这返回的是不可变空列表,再传给reverse()会报错 —— 所以仍需判空后处理 - 不要依赖 try-catch 捕获
NullPointerException来兜底,这是反模式
想反转并生成新 List?别用 reverse(),改用 Stream + Collectors.collectingAndThen
Collections.reverse() 是 in-place 操作(原地修改),无法返回新集合。如果需要保留原始顺序、仅获取一个反转副本,必须手动复制。
性能影响:对大列表,new ArrayList(original) + reverse() 比纯 Stream 方式略快,但可读性差;Stream 写法更函数式,但要注意中间操作开销。
- 推荐副本方案(Java 10+):
List
reversed = new ArrayList<>(original); Collections.reverse(reversed); - Stream 方案(需反转索引):
List
reversed = IntStream.range(0, original.size()) .mapToObj(i -> original.get(original.size() - 1 - i)) .collect(Collectors.toList()); - 注意:Stream 方案在
LinkedList上性能极差(get(i)是 O(n)),务必只用于ArrayList或数组
并发环境不能直接用 Collections.reverse()
该方法没有任何同步机制。若多个线程同时读写同一个 List,即使只调用 reverse(),也会引发 ConcurrentModificationException 或数据错乱(尤其在 ArrayList resize 过程中被反转)。
使用场景:后台定时任务批量处理共享缓存列表,或 Web 应用中将列表存在 ServletContext 中被多请求访问。
- 安全做法:用
Collections.synchronizedList()包装,但仅包装不等于线程安全 ——reverse()仍是多步操作,需额外加锁 - 更合理方案:反转操作前加
synchronized(list)块,或改用不可变集合(如ImmutableListfrom Guava)+ 构建新反转副本 - 别试图用
CopyOnWriteArrayList:它适合读多写少,但reverse()会触发全量复制,内存和 CPU 开销巨大
实际中最容易被忽略的,是 Collections.reverse() 对输入类型的隐含要求和并发下的非原子性 —— 它看起来像一个“安全函数”,但一旦脱离单线程、可变 ArrayList 这个舒适区,问题就会立刻浮现。










