如何使用Java Streams对Map值按指定区间分组并统计元素数量?
嘿,这个需求用Java Streams完全可以优雅实现,我给你拆解一下具体步骤,附上完整代码和解释:
使用Java Streams实现按区间分组统计
我们要解决两个核心问题:一是把每个数值映射到对应的区间,二是确保所有要求的区间都出现在结果中(哪怕该区间没有元素,也要显示0)。
步骤1:准备原始数据
先初始化题目给出的Map:
Map<String, BigDecimal> data = new HashMap<>(); data.put("A", new BigDecimal(12)); data.put("B", new BigDecimal(23)); data.put("C", new BigDecimal(67)); data.put("D", new BigDecimal(99));
步骤2:定义区间规则
我们可以先明确所有需要的区间,以及每个数值归属区间的判断逻辑。这里把区间和判断条件绑定,方便后续统一处理:
// 定义所有目标区间,同时指定每个区间的判断规则(保持顺序) List<Map.Entry<String, Predicate<BigDecimal>>> intervals = Arrays.asList( new AbstractMap.SimpleEntry<>("0-25", num -> num.compareTo(BigDecimal.ZERO) >= 0 && num.compareTo(new BigDecimal(25)) <= 0), new AbstractMap.SimpleEntry<>("26-50", num -> num.compareTo(new BigDecimal(26)) >= 0 && num.compareTo(new BigDecimal(50)) <= 0), new AbstractMap.SimpleEntry<>("51-75", num -> num.compareTo(new BigDecimal(51)) >= 0 && num.compareTo(new BigDecimal(75)) <= 0), new AbstractMap.SimpleEntry<>("76-100", num -> num.compareTo(new BigDecimal(76)) >= 0 && num.compareTo(new BigDecimal(100)) <= 0) );
注意:BigDecimal的比较必须用
compareTo方法,不能直接用>/<,因为它是对象类型,直接比较会出错。
步骤3:统计各区间元素数量
用Collectors.groupingBy把数值按区间分组,同时统计每组的元素个数:
// 先统计有数据的区间的元素数量 Map<String, Long> counted = data.values().stream() .collect(Collectors.groupingBy( num -> intervals.stream() .filter(entry -> entry.getValue().test(num)) .map(Map.Entry::getKey) .findFirst() .orElse(null), // 处理不在任何区间的数值(这里题目数据都在区间内,可忽略) Collectors.counting() ));
步骤4:补全空区间的0值
默认的groupingBy只会生成有元素的区间键,所以我们需要把所有目标区间都加入结果,没有元素的填0:
// 构建最终结果,确保所有区间都存在,空区间填充0 Map<String, Long> result = intervals.stream() .map(Map.Entry::getKey) .collect(Collectors.toMap( key -> key, key -> counted.getOrDefault(key, 0L), (oldVal, newVal) -> oldVal, // 合并逻辑,这里不会触发 LinkedHashMap::new // 可选:保持区间的定义顺序 ));
验证结果
运行后输出结果:
0-25: 2 26-50: 0 51-75: 1 76-100: 1
优化写法(可选)
如果觉得区间判断逻辑写在流里太繁琐,可以抽成一个单独的方法,让代码更清晰:
private static String getInterval(BigDecimal num) { if (num.compareTo(new BigDecimal(25)) <= 0) return "0-25"; else if (num.compareTo(new BigDecimal(50)) <= 0) return "26-50"; else if (num.compareTo(new BigDecimal(75)) <= 0) return "51-75"; else if (num.compareTo(new BigDecimal(100)) <= 0) return "76-100"; else return null; // 处理超出范围的数值 }
然后分组时直接调用这个方法:
Map<String, Long> counted = data.values().stream() .collect(Collectors.groupingBy( YourClassName::getInterval, Collectors.counting() ));
内容的提问来源于stack exchange,提问作者Vipin
相关产品推荐
相关产品推荐

