JAVA 8 Stream API 对Stream进行分组、分区

这篇文章展示了如何使用 Streams API 中的 Collector 及 groupingBy 和 partitioningBy 来对流中的元素进行分组和分区。

思考一下 Employee 对象流,每个对象对应一个名字、城市和销售数量,如下表所示:

+----------+------------+-----------------+
| Name     | City       | Number of Sales |
+----------+------------+-----------------+
| Alice    | London     | 200             |
| Bob      | London     | 150             |
| Charles  | New York   | 160             |
| Dorothy  | Hong Kong  | 190             |
+----------+------------+-----------------+

分组

首先,我们利用(lambda表达式出现之前的)命令式风格Java 程序对流中的雇员按城市进行分组:

1
2
3
4
5
6
7
8
9
10
Map<String, List<Employee>> result = new HashMap<>();
for (Employee e : employees) {
   String city = e.getCity();
   List<Employee> empsInCity = result.get(city);
   if (empsInCity == null ) {
     empsInCity = new ArrayList<>();
     result.put(city, empsInCity);
   }
   empsInCity.add(e);
}

你可能很熟悉写这样的代码,你也看到了,一个如此简单的任务就需要这么多代码!

而在 Java 8 中,你可以使用 groupingBy 收集器,一条语句就能完成相同的功能,像这样:

1
2
Map<String, List<Employee>> employeesByCity =
   employees.stream().collect(groupingBy(Employee::getCity));

结果如下面的 map 所示:

1
{New York=[Charles], Hong Kong=[Dorothy], London=[Alice, Bob]}

还可以计算每个城市中雇员的数量,只需传递一个计数收集器给 groupingBy 收集器。第二个收集器的作用是在流分类的同一个组中对每个元素进行递归操作。

1
2
Map<String, Long> numEmployeesByCity =
   employees.stream().collect(groupingBy(Employee::getCity, counting()));

结果如下面的 map 所示:

1
{New York= 1 , Hong Kong= 1 , London= 2 }

顺便提一下,该功能与下面的 SQL 语句是等同的:

1
select city, count(*) from Employee group by city

另一个例子是计算每个城市的平均年龄,这可以联合使用 averagingInt 和 groupingBy 收集器:

1
2
3
Map<String, Double> avgSalesByCity =
   employees.stream().collect(groupingBy(Employee::getCity,
                                averagingInt(Employee::getNumSales)));

结果如下 map 所示:

1
{New York= 160.0 , Hong Kong= 190.0 , London= 175.0 }

 

分区

分区是一种特殊的分组,结果 map 至少包含两个不同的分组——一个true,一个false。例如,如果想找出最优秀的员工,你可以将所有雇员分为两组,一组销售量大于 N,另一组小于 N,使用 partitioningBy 收集器:

1
2
Map<Boolean, List<Employee>> partitioned =
   employees.stream().collect(partitioningBy(e -> e.getNumSales() > 150 ));

输出如下结果:

1
{ false =[Bob], true =[Alice, Charles, Dorothy]}

你也可以将 groupingBy 收集器传递给 partitioningBy 收集器来将联合使用分区和分组。例如,你可以统计每个分区中的每个城市的雇员人数:

1
2
3
Map<Boolean, Map<String, Long>> result =
   employees.stream().collect(partitioningBy(e -> e.getNumSales() > 150 ,
                                groupingBy(Employee::getCity, counting())));

这样会生成一个二级 Map:

1
{ false ={London= 1 }, true ={New York= 1 , Hong Kong= 1 , London= 1 }}
原文链接:  javacodegeeks  翻译:  ImportNew.com  paddx
译文链接:  http://www.importnew.com/17313.html
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值