在 Java 中,Stream API 为我们提供了一种处理数据的现代化方式。但在进行过滤、映射和转换操作之后,我们通常需要将结果收集到一个集合、一个 Map,甚至是单个值中。
这就是 Collectors 大显身手的地方。
- Collector 是一个对象,它定义了如何将 Stream 中的元素累积到最终结果中。
- Collectors 工具类提供了现成的静态方法供我们使用,因此我们几乎不需要自己编写收集器。
实现 City 类的 Java 程序
让我们首先定义一个 City 类来准备我们的数据模型。
public class City {
private String name;
private double temperature;
// 带参数的构造函数
public City(String name, double temperature) {
this.name = name;
this.temperature = temperature;
}
// Getter 方法
public String getName() { return name; }
public Double getTemperature() { return temperature; }
// 重写 toString 方法以便于显示
@Override
public String toString() {
return name + " --> " + temperature;
}
}
我们还需要创建一个辅助方法来准备样本数据:
private static List prepareTemperature() {
List cities = new ArrayList();
cities.add(new City("New Delhi", 33.5));
cities.add(new City("Mexico", 14));
cities.add(new City("New York", 13));
cities.add(new City("Dubai", 43));
cities.add(new City("London", 15));
cities.add(new City("Alaska", 1));
cities.add(new City("Kolkata", 30));
cities.add(new City("Sydney", 11));
cities.add(new City("Mexico", 14)); // 重复数据
cities.add(new City("Dubai", 43)); // 重复数据
return cities;
}
常用的 Collectors 方法
1. toList()
toList() 方法将输入元素转换为一个新的 List,并返回一个 Collector。
下面的程序演示了如何实现 toList() 方法:
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
public class Main {
public static void main(String[] args)
{
System.out.println(prepareTemperature().stream()
.filter(f -> f.getTemperature() > 10)
.map(f -> f.getName())
.collect(Collectors.toList()));
}
}
输出:
> [New Delhi, Mexico, New York, Dubai, London, Kolkata, Sydney, Mexico, Dubai]
2. toSet()
toSet() 方法将输入元素转换为一个新的 Set 并返回一个 Collector。这个方法会返回一个不包含重复元素的集合。
下面的程序演示了如何实现 toSet() 方法:
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
public class Main {
public static void main(String[] args)
{
System.out.println(prepareTemperature()
.stream()
.filter(f -> f.getTemperature() > 10)
.map(f -> f.getName())
.collect(Collectors.toSet()));
}
}
输出:
> [New York, New Delhi, London, Mexico, Kolkata, Dubai, Sydney]
在这里,我们可以注意到输出结果中 Mexico 和 Dubai 没有重复出现。
3. toCollection(Supplier collectionFactory)
INLINECODEbc570311 方法将 Stream 元素收集到用户定义的集合中,这与使用默认类型的 INLINECODE16b24b13 或 INLINECODE27d3ddb8 不同。其中,INLINECODE3ec1f0dd 是目标集合类型,T 是元素类型。
下面的程序演示了如何实现 toCollection() 方法:
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.stream.Collectors;
public class Main {
public static void main(String[] args)
{
System.out.println(prepareTemperature()
.stream()
.map(f -> f.getName())
.collect(Collectors.toCollection(LinkedList::new)));
}
}
输出:
> [New Delhi, Mexico, New York, Dubai, London, Alaska, Kolkata, Sydney, Mexico, Dubai]
4. toMap()
toMap(Function keyMapper, Function valueMapper) 方法将元素转换为一个 Map。
下面的程序演示了如何实现 toMap() 方法:
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
import java.util.Map;
public class Main {
public static void main(String[] args)
{
// 这里,我们将名称定义为 Key,温度定义为 Value
Map cityTempMap = prepareTemperature()
.stream()
.filter(city -> city.getTemperature() > 10)
.collect(Collectors.toMap(City::getName, City::getTemperature));
System.out.println(cityTempMap);
}
}
输出:
> {New Delhi=33.5, Mexico=14.0, New York=13.0, Dubai=43.0, London=15.0, Kolkata=30.0, Sydney=11.0}
注意:如果 Stream 中存在重复的 Key(例如上面的例子中有两个 "Mexico"),直接使用 toMap 可能会抛出 IllegalStateException。为了避免这种情况,我们需要提供合并函数来处理冲突。