一:概述
在这个博客文章中,我们将探讨如何使用Java Stream API对某个字段的去重和排序进行操作。我们将介绍几种不同的方法,每种方法都会有一个实际的案例来展示如何实现。
二:具体说明
<1>Collectors.toCollection()
第一种方法是使用Collectors.toCollection()
方法来指定一个自定义的集合类型,例如TreeSet
,它可以帮助我们自动去重和排序。
案例:
假设我们有一个学生列表,每个学生有学号和姓名两个字段,我们想要根据学号进行去重和排序。
import java.util.*;
import java.util.stream.Collectors;
public class Main {
public static void main(String[] args) {
List<Student> students = Arrays.asList(
new Student("张三", "001"),
new Student("李四", "002"),
new Student("王五", "001"),
new Student("赵六", "003")
);
List<String> distinctStudentIds = students.stream()
.collect(Collectors.toCollection(TreeSet::new))
.stream()
.map(Student::getStudentId)
.collect(Collectors.toList());
System.out.println(distinctStudentIds);
}
public static class Student {
private String name;
private String studentId;
public Student(String name, String studentId) {
this.name = name;
this.studentId = studentId;
}
public String getName() {
return name;
}
public String getStudentId() {
return studentId;
}
}
}
在这个案例中,我们首先使用Collectors.toCollection(TreeSet::new)
方法将学生列表转换为一个去重的TreeSet
集合。然后,我们再次调用stream()
方法来获取去重后的学生列表,并通过map()
方法提取出学号。最后,我们使用collect()
方法将结果转换为列表。
<2>Collectors.collectingAndThen()
第二种方法是使用Collectors.collectingAndThen()
方法来指定一个自定义的收集器。这种方法更加灵活,可以让我们在收集结果之前执行一些额外的操作。
案例:
假设我们有一个商品列表,每个商品有名称和价格两个字段,我们想要根据名称进行去重,并根据价格进行排序。
import java.util.*;
import java.util.stream.Collectors;
public class Main {
public static void main(String[] args) {
List<Product> products = Arrays.asList(
new Product("电视", 2000),
new Product("冰箱", 3000),
new Product("电视", 1500),
new Product("洗衣机", 1800)
);
List<Product> distinctAndSortedProducts = products.stream()
.collect(Collectors.collectingAndThen(
Collectors.toCollection(TreeSet::new),
list -> {
list.forEach(product -> {
product.setPrice(product.getPrice());
});
return list;
}
))
.stream()
.collect(Collectors.toList());
System.out.println(distinctAndSortedProducts);
}
public static class Product {
private String name;
private double price;
public Product(String name, double price) {
this.name = name;
this.price = price;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
}
}
在这个案例中,我们首先使用Collectors.toCollection(TreeSet::new)
方法将商品列表转换为一个去重的TreeSet
,然后对这个TreeSet
进行额外的操作。在这个案例中,我们想要在去重后对每个商品的价格进行排序。
import java.util.*;
import java.util.stream.Collectors;
public class Main {
public static void main(String[] args) {
List<Product> products = Arrays.asList(
new Product("电视", 2000),
new Product("冰箱", 3000),
new Product("电视", 1500),
new Product("洗衣机", 1800)
);
List<Product> distinctAndSortedProducts = products.stream()
.collect(Collectors.collectingAndThen(
Collectors.toCollection(TreeSet::new),
productsTreeSet -> {
productsTreeSet.forEach(product -> {
product.setPrice(product.getPrice());
});
return productsTreeSet;
}
))
.stream()
.collect(Collectors.toList());
System.out.println(distinctAndSortedProducts);
}
public static class Product {
private String name;
private double price;
public Product(String name, double price) {
this.name = name;
this.price = price;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
}
}
在这个代码中,collectingAndThen
接受两个参数:一个收集器和一个函数。第一个参数(收集器)负责去重和建立一个TreeSet
,第二个参数(函数)则负责对TreeSet
中的元素进行额外的操作。在这个例子中,我们没有实际改变TreeSet
中的元素,但是通过调用setPrice
方法,我们确保了每个产品的价格都被访问并保留了原来的值。然后,我们再次将TreeSet
转换为列表。
<3>Collectors.toMap()
第三种方法是使用Collectors.toMap()
来创建一个映射(Map),其中键是我们要去重的字段,值是原始对象。这种方法允许我们以键值对的形式去重,并且可以自定义键的生成方式。
案例:
假设我们有一个员工列表,每个员工有一个ID和一个姓名,我们想要根据ID去重并排序。
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
public class Main {
public static void main(String[] args) {
List<Employee> employees = Arrays.asList(
new Employee("Alice", 1),
new Employee("Bob", 2),
new Employee("Alice", 1),
new Employee("Charlie", 3)
);
List<Employee> distinctAndSortedEmployees = employees.stream()
.collect(Collectors.toMap(
Employee::getId,
Function.identity(),
(existing, replacement) -> existing
))
.values()
.stream()
.collect(Collectors.toList());
System.out.println(distinctAndSortedEmployees);
}
public static class Employee {
private String name;
private int id;
public Employee(String name, int id) {
this.name = name;
this.id = id;
}
public String getName() {
return name;
}
public int getId() {
return id;
}
}
}
在这个案例中,我们使用Collectors.toMap()
来创建一个以员工ID为键的映射。这里我们使用了三个参数的toMap
方法,第一个参数是键的映射函数,第二个参数是值的映射函数,第三个参数是当键已经存在时如何处理新值的方法。在这个例子中,我们使用Function.identity()
作为值的映射函数,因为我们需要保留原始的员工对象。当键已经存在时,我们选择保留现有的值((existing, replacement) -> existing
)。最后,我们通过调用values()
方法来获取映射中的值,并将其转换为列表。
<4>Collectors.groupingBy()
第四种方法是使用Collectors.groupingBy()
来对流中的元素进行分组,然后从分组中提取出需要的字段。这种方法适用于复杂的数据结构,可以在分组后对每个组执行去重和排序的操作。
案例:
假设我们有一个订单列表,每个订单有一个商品ID和一个数量,我们想要根据商品ID去重并排序每个商品的总数量。
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
public class Main {
public static void main(String[] args) {
List<Order> orders = Arrays.asList(
new Order("TV", 2),
new Order("Fridge", 1),
new Order("TV", 1),
new Order("Washing Machine", 3)
);
Map<String, Long> productToTotalQuantity = orders.stream()
.collect(Collectors.groupingBy(
Order::getProductId,
Collectors.summingLong(Order::getQuantity)
))
.entrySet().stream()
.collect(Collectors.toMap(
Map.Entry::getKey,
Map.Entry::getValue,
Long::sum
));
productToTotalQuantity.forEach((productId, totalQuantity) ->
System.out.println(productId + " : " + totalQuantity));
}
public static class Order {
private String productId;
private int quantity;
public Order(String productId, int quantity) {
this.productId = productId;
this.quantity = quantity;
}
public String getProductId() {
return productId;
}
public int getQuantity() {
return quantity;
}
}
}
在这个案例中,我们首先使用groupingBy
对订单按照商品ID进行分组,然后使用summingLong
来计算每个分组中订单的数量总和。接着,我们再次使用entrySet().stream()
将分组转换为映射项的流,然后使用toMap
来将分组和数量总和转换为一个不重复的映射。在这个映射中,我们通过Long::sum
来处理相同商品ID的分组数量总和。
<5>总结
在本博客文章中,我们探讨了四种使用Java Stream API对某个字段进行去重和排序的方法。每种方法都有对应的实际案例来展示如何实现。Collectors.toCollection()
方法简单直接,适用于基本数据类型;Collectors.collectingAndThen()
方法更加灵活,可以在去重后对元素进行自定义操作;Collectors.toMap()
方法适用于需要以键值对形式去重的情况;而Collectors.groupingBy()
方法则适用于更复杂的数据结构,可以在分组后对每个组执行去重和排序的操作。根据不同的需求,我们可以选择合适的方法来实现去重和排序。