
本文详解如何使用 java 的 stream api 和 comparator 链式调用,基于频次(降序)和数值本身(升序)对整数列表进行稳定排序,并修正常见类型不匹配错误。
本文详解如何使用 java 的 stream api 和 comparator 链式调用,基于频次(降序)和数值本身(升序)对整数列表进行稳定排序,并修正常见类型不匹配错误。
在实际开发中,常需对列表按“元素出现频率”排序:高频优先;当频率相同时,再按元素值升序排列(如 42 出现 4 次、5 和 17 各出现 2 次,则结果应为 [42, 42, 42, 42, 5, 5, 17, 17])。原始代码出错的根本原因在于:Comparator 的泛型类型与待排序 List 的元素类型不一致——items 是 List<Integer>,但 Comparator 却定义为 Comparator<Map.Entry<Integer, Integer>>,导致 Collections.sort(items, comp) 编译失败。
正确思路是:Comparator 应接收两个 Integer(即列表中的元素),并在比较时动态查表获取其频次。推荐使用现代 Java(8+)的函数式写法,简洁、安全且可读性强:
import java.util.*;
import java.util.stream.Collectors;
import java.util.function.Function;
public static void sortByFreq(List<Integer> items) {
// 步骤1:构建频次映射 —— 使用 Long 避免 int 溢出风险,且与 Collectors.counting() 类型匹配
Map<Integer, Long> counter = items.stream()
.collect(Collectors.groupingBy(Function.identity(), Collectors.counting()));
// 步骤2:原地排序 —— 先按频次降序(.reversed()),再按数值升序(Function.identity())
items.sort(
Comparator.comparing((Integer i) -> counter.get(i))
.reversed()
.thenComparing(Function.identity())
);
}✅ 关键要点说明:
- items.sort(...) 是 List 接口的默认方法,直接操作原列表,无需额外 Collections.sort;
- Comparator.comparing((Integer i) -> counter.get(i)) 明确声明入参为 Integer,与 items 元素类型一致,彻底规避类型推断错误;
- .reversed() 将频次比较转为降序(默认升序),符合“高频优先”需求;
- .thenComparing(Function.identity()) 在频次相等时,按元素自身值升序排列(即 5 < 17,故 5 排在 17 前);
- 使用 Long 作为频次类型(而非 Integer)更健壮,尤其在大数据量场景下防止计数溢出。
⚠️ 注意事项:
- 切勿在 Comparator 中直接对 Map.Entry 比较后作用于 List<Integer>——这是类型系统不兼容的根源;
- 若需保持原列表不变,应先 new ArrayList<>(items) 创建副本再排序;
- counter.get(i) 在 i 必然存在(因由 items 构建),无需空值检查,性能无损。
该方案逻辑清晰、零冗余循环、符合函数式编程范式,是解决“频次+次级键”多级排序问题的标准实践。

















