Java 8 Streams API:對Stream分組和分區

jopen 8年前發布 | 21K 次閱讀 Java8 API Java開發

這篇文章展示了如何使用 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 程序對流中的雇員按城市進行分組:

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 收集器,一條語句就能完成相同的功能,像這樣:

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

結果如下面的 map 所示:

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

還可以計算每個城市中雇員的數量,只需傳遞一個計數收集器給 groupingBy 收集器。第二個收集器的作用是在流分類的同一個組中對每個元素進行遞歸操作。

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

結果如下面的 map 所示:

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

順便提一下,該功能與下面的 SQL 語句是等同的:

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

另一個例子是計算每個城市的平均年齡,這可以聯合使用 averagingInt 和 groupingBy 收集器:

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

結果如下 map 所示:

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

分區

分區是一種特殊的分組,結果 map 至少包含兩個不同的分組——一個true,一個false。例如,如果想找出最優秀的員工,你可以將所有雇員分為兩組,一組銷售量大于 N,另一組小于 N,使用 partitioningBy 收集器:

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

輸出如下結果:

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

你也可以將 groupingBy 收集器傳遞給 partitioningBy 收集器來將聯合使用分區和分組。例如,你可以統計每個分區中的每個城市的雇員人數:

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

這樣會生成一個二級 Map:

{false={London=1}, true={New York=1, Hong Kong=1, London=1}}

原文鏈接: javacodegeeks 翻譯:ImportNew.com -paddx

譯文鏈接:[]

來自: http://www.importnew.com/17313.html

 本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!