本文翻译自国外论坛 medium,原文地址:salithachathuranga94.medium.com/java-8-stre…
Java 得 Streams 流随着 JDK 1.8 的发布而呈现,是对调集(Collection)目标功用的增强,它专注于对调集目标进行各种聚合或者分组操作。
本文我会给咱们具体讲解下 Streams 流相关的分组操作。
假定咱们有一组学生,需求按年纪对他们进行分组。依照 Java 得传统办法,咱们可能需求好几个过程。
假如我说,运用流分组,咱们能够用 1 行代码来完结此操作呢?是不是很奇特?让咱们来看看。
Streams 得 collect 办法承受一个 Collector 参数。该办法能够接收分组目标。 Collectors 类中分组相关的 3 个办法如下所示,
// 1st method
public static <T, K> Collector<T, ?, Map<K, List<T>>> groupingBy(Function<? super T, ? extends K> var0)
// 2nd method
public static <T, K, A, D> Collector<T, ?, Map<K, D>> groupingBy(Function<? super T, ? extends K> var0, Collector<? super T, A, D> var1)
// 3rd method
public static <T, K, D, A, M extends Map<K, D>> Collector<T, ?, M> groupingBy(Function<? super T, ? extends K> var0, Supplier<M> var1, Collector<? super T, A, D> var2)
一、运用 Function 进行分组
这儿咱们将运用分组操作的第一个办法,它只承受 Function 作为办法参数。
假定咱们有一份职工名单,
Employee e1 = new Employee("John", 38);
Employee e2 = new Employee("Tim", 33);
Employee e3 = new Employee("Andrew", 33);
Employee e4 = new Employee("Peter", 38);
Employee e5 = new Employee("Nathan", 22);
Employee e6 = new Employee("George", 23);
List<Employee> employees = Arrays.asList(e1, e2, e3, e4, e5, e6);
该职工有名字和年纪。咱们需求按年纪对这些职工目标进行分组。怎么完成这一目标?
Map<Integer, List<Employee>> employeesByAge = employees.stream()
.collect(Collectors.groupingBy(Employee::getAge));
咱们这儿只需求一个函数
Employee::getAge — 依照职工年纪进行分组
输出:
{
33=[
Employee{age=33, name='Tim'},
Employee{age=33, name='Andrew'}
],
22=[
Employee{age=22, name='Nathan'}
],
38=[
Employee{age=38, name='John'},
Employee{age=38, name='Peter'}
],
23=[
Employee{age=23, name='George'}
]
}
运用简略的 1 行代码咱们就做到了!
引荐博主开源的 H5 商城项目waynboot-mall,这是一套全部开源的微商城项目,包含三个项目:运营后台、H5 商城前台和服务端接口。完成了商城所需的首页展示、产品分类、产品概况、产品 sku、分词查找、购物车、结算下单、付出宝/微信付出、收单评论以及完善的后台办理等一系列功用。 技能上基于最新得 Springboot3.0、jdk17,整合了 MySql、Redis、RabbitMQ、ElasticSearch 等常用中间件。分模块设计、简洁易保护,欢迎咱们点个 star、重视博主。
github 地址:github.com/wayn111/way…
二、运用 Function 和 Collector 进行分组
这儿咱们将运用分组操作的第二个办法,它承受 Function 和 Collector 作为办法参数。
对自定义目标进行分组
举例 1️⃣
假定咱们有一个项目列表。咱们的 pojo 是具有称号、价格和数量的 Item。
class Item {
private String name;
private int qty;
private BigDecimal price;
public Item(String name, int qty, BigDecimal price) {
this.name = name;
this.qty = qty;
this.price = price;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getQty() {
return qty;
}
public void setQty(int qty) {
this.qty = qty;
}
public BigDecimal getPrice() {
return price;
}
public void setPrice(BigDecimal price) {
this.price = price;
}
@Override
public String toString() {
return "Item{" +
"name='" + name + '\'' +
", qty=" + qty +
", price=" + price +
'}';
}
}
项目列表将是这样的
Arrays.asList(
new Item("apple", 10, new BigDecimal("9.99")),
new Item("banana", 20, new BigDecimal("19.99")),
new Item("orange", 10, new BigDecimal("29.99")),
new Item("watermelon", 10, new BigDecimal("29.99")),
new Item("papaya", 20, new BigDecimal("9.99")),
new Item("apple", 10, new BigDecimal("9.99")),
new Item("banana", 10, new BigDecimal("19.99")),
new Item("apple", 20, new BigDecimal("9.99"))
);
咱们需求按项目称号进行分组,然后统计每个分组得总数量。尽管这儿是目标,但咱们只需求项目称号以及对应总数量。
代码如下:
Map<String, Integer> result = items.stream()
.collect(Collectors.groupingBy(Item::getName, Collectors.summingInt(Item::getQty)));
Item::getName — 依照称号分组
Collectors.summingInt(Item::getQty) — 对分组后调集按数量求和
输出:
{
papaya=20,
orange=10,
banana=30,
apple=40,
watermelon=10
}
举例 2️⃣
假定咱们有一份职工名单,咱们的职工有名字和年纪。
public class Employee {
int age;
String name;
public Employee(String name, int age) {
this.name = name;
this.age = age;
}
public int getAge() {
return age;
}
public String getName() {
return name;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Employee)) return false;
Employee employee = (Employee) o;
return age == employee.age && name.equals(employee.name);
}
@Override
public int hashCode() {
return Objects.hash(age, name);
}
@Override
public String toString() {
return "Employee{" +
"age=" + age +
", name='" + name + '\'' +
'}';
}
}
咱们需求按年纪对职工名字进行分组。怎么运用 Stream 流来做到这一点?
Map<Integer, List<String>> employeeNamesByAge = employees.stream()
.collect(Collectors.groupingBy(
Employee::getAge,
Collectors.mapping(Employee::getName, Collectors.toList())
)
);
Employee::getAge — 依照职工年纪分组
Collectors.mapping(Employee::getName, Collectors.toList()) — 将分组后的职工列表转化为名字列表
输出:
{
33=[Tim, Andrew],
22=[Nathan],
38=[John, Peter],
23=[George]
}
这两个比如都很好得对应了 Collector 类的第二个办法。
三、按 Function、Supplier 和 Collector 分组
这儿咱们将运用分组操作的第三种办法,它承受 Function、Supplier 和 Collector 作为办法参数。
假定咱们需求对产品按价格分组展示产品称号。咱们能够做些什么来完成它?
List<Item> items = getItemsList();
Map<BigDecimal, Set<String>> result = items.stream()
.collect(
Collectors.groupingBy(
Item::getPrice,
Collectors.mapping(Item::getName, Collectors.toSet())
)
);
Item::getPrice — 按价格进行分组
Collectors.mapping(Item::getName, Collectors.toSet()) — 将分组后得产品列表转化为称号列表
假如咱们需求对分组后的产品称号按价格进行排序?咱们该怎么做。
依据分组操作的第三种办法,咱们只能供给一个新的 TreeMap 参数。
List<Item> items = getItemsList();
Map<BigDecimal, Set<String>> sortedItemsByPrice = items.stream()
.collect(
Collectors.groupingBy(
Item::getPrice,
TreeMap::new,
Collectors.mapping(Item::getName, Collectors.toSet())
)
);
输出:
{
9.99=[papaya, apple],
19.99=[banana],
29.99=[orange, watermelon]
}
依照这种办法,咱们也能够对职工列表使用相同的规则!咱们能够按年纪对它们进行分组并排序
Map<Integer, Set<String>> sortedEmployeesByAge = employees.stream()
.collect(Collectors.groupingBy(
Employee::getAge,
TreeMap::new,
Collectors.mapping(Employee::getName, Collectors.toSet())
)
);
输出:
{
22=[Nathan],
23=[George],
33=[Tim, Andrew],
38=[John, Peter]
}
这就是第三种办法的相关用途,咱们能够简略地经过 Supplier 参数来完成分组排序逻辑。
最后
我已经在本文中尽可能具体地解说了 Collectors 类分组操作相关的 3 个办法,希望您能在日常编程中理解并运用它。
重视公众号【waynblog】每周共享技能干货、开源项目、实战经验、高效开发工具等,您的重视将是我的更新动力!