1. Overview
1.概述
In this tutorial, we’re going to convert a List<E> into a Map<K, List<E>>. We’ll achieve this with Java’s Stream API and the Supplier functional interface.
在本教程中,我们将把List<E>转换成Map<K, List<E>>。我们将通过Java的Stream API和Supplier功能接口实现这一目标。
2. Supplier in JDK 8
2.JDK 8中的Supplier
Supplier is often used as a factory. A method can take a Supplier as input and constrains the type using a bounded wildcard type, then the client can pass in a factory that creates any subtype of the given type.
Supplier经常被用作工厂。一个方法可以接受一个Supplier作为输入,并使用一个有边界的通配符类型来约束该类型,然后客户端可以传入一个工厂,该工厂可以创建给定类型的任何子类型。
Besides that, the Supplier can perform a lazy generation of values.
除此之外,Supplier可以进行快速生成值。
3. Converting the List to Map
3.将List转换为Map
The Stream API provides support for List manipulation. One such example is the Stream#collect method. However, there isn’t a way in the Stream API methods to give Suppliers to the downstream parameters directly.
Stream API提供了对List操作的支持。其中一个例子是Stream#collect方法。然而,在Stream API方法中并没有办法直接给下游参数提供Suppliers。
In this tutorial, we’ll take a look at the Collectors.groupingBy, Collectors.toMap, and Stream.collect methods with example code snippets. We’ll focus on methods that allow us to use a custom Supplier.
在本教程中,我们将通过示例代码片段来了解Collectors.groupingBy、Collectors.toMap,以及Stream.collect方法。我们将重点讨论那些允许我们使用自定义Supplier的方法。
In this tutorial, we’ll process a String List collections in the following examples:
在本教程中,我们将在以下例子中处理一个String List集合。
List source = Arrays.asList("List", "Map", "Set", "Tree");
We’ll aggregate the above list into a map whose key is the string’s length. When we’re done, we’ll have a map that looks like:
我们将把上面的列表汇总成一个地图,其键是字符串的长度。当我们完成后,我们会有一个看起来像的地图。
{
3: ["Map", "Set"],
4: ["List", "Tree"]
}
3.1. Collectors.groupingBy()
3.1.Collectors.groupingBy()
With Collectors.groupingBy, we can convert a Collection to a Map with a specific classifier. The classifier is an element’s attribute, we’ll use this attribute to incorporate the elements into different groups:
通过Collectors.groupingBy,我们可以将一个Collection转换为一个带有特定分类器的Map。分类器是一个元素的属性,我们将使用这个属性将元素纳入不同的组。
public Map<Integer, List> groupingByStringLength(List source,
Supplier<Map<Integer, List>> mapSupplier,
Supplier<List> listSupplier) {
return source.stream()
.collect(Collectors.groupingBy(String::length, mapSupplier, Collectors.toCollection(listSupplier)));
}
We can validate it works with:
我们可以验证它与。
Map<Integer, List> convertedMap = converter.groupingByStringLength(source, HashMap::new, ArrayList::new);
assertTrue(convertedMap.get(3).contains("Map"));
3.2. Collectors.toMap()
3.2.Collectors.toMap()
The Collectors.toMap method reduces the elements within a stream into a Map.
Collectors.toMap方法将一个流中的元素还原成Map.。
We start by defining the method with source string and both List and Map suppliers:
我们先用源字符串和List和Map供应商定义方法。
public Map<Integer, List> collectorToMapByStringLength(List source,
Supplier<Map<Integer, List>> mapSupplier,
Supplier<List> listSupplier)
We then define how to obtain the key and the value out of an element. For that we make use of two new functions:
然后,我们定义如何从一个元素中获得键和值。为此,我们使用了两个新的函数。
Function<String, Integer> keyMapper = String::length;
Function<String, List> valueMapper = (element) -> {
List collection = listSupplier.get();
collection.add(element);
return collection;
};
Finally, we define a function that is called upon key conflict. In this case, we want to combine the contents of both:
最后,我们定义一个在钥匙冲突时被调用的函数。在这种情况下,我们要结合两者的内容。
BinaryOperator<List> mergeFunction = (existing, replacement) -> {
existing.addAll(replacement);
return existing;
};
Putting everything together, we get:
把所有东西放在一起,我们得到。
source.stream().collect(Collectors.toMap(keyMapper, valueMapper, mergeFunction, mapSupplier))
Note that most of the time the functions we define are anonymous inline functions inside the argument list of the method.
请注意,大多数时候,我们定义的函数都是方法的参数列表内的匿名内联函数。
Let’s test it:
让我们来测试一下。
Map<Integer, List> convertedMap = converter.collectorToMapByStringLength(source, HashMap::new, ArrayList::new);
assertTrue(convertedMap.get(3).contains("Map"));
3.3. Stream.collect()
3.3.Stream.collect()
The Stream.collect method can be used to reduce the elements in a stream into a Collection of any type.
Stream.collect方法可用于将流中的元素还原成任何类型的Collection。
For that, we also need to define a method with both List and Map suppliers that will be called once a new collection is needed:
为此,我们还需要定义一个同时具有List和Map供应商的方法,一旦需要一个新的集合就会被调用。
public Map<Integer, List> streamCollectByStringLength(List source,
Supplier<Map<Integer, List>> mapSupplier,
Supplier<List> listSupplier)
We then move to define an accumulator that, given the key to the element, gets an existing list, or creates a new one, and adds the element to the response:
然后我们定义一个accumulator,给定元素的键,获得一个现有的列表,或创建一个新的列表,并将元素添加到响应中。
BiConsumer<Map<Integer, List>, String> accumulator = (response, element) -> {
Integer key = element.length();
List values = response.getOrDefault(key, listSupplier.get());
values.add(element);
response.put(key, values);
};
We finally move to combine the values generated by the accumulator function:
我们最后移动到合并由累加器函数产生的值。
BiConsumer<Map<Integer, List>, Map<Integer, List>> combiner = (res1, res2) -> {
res1.putAll(res2);
};
Putting everything together, we then just call the collect method on the stream of our elements:
把所有东西放在一起,然后我们只需在我们的元素流上调用collect方法。
source.stream().collect(mapSupplier, accumulator, combiner);
Note that most of the time the functions we define are anonymous inline functions inside the argument list of the method.
请注意,大多数时候,我们定义的函数都是方法的参数列表内的匿名内联函数。
The test result will be the same as the other two methods:
测试结果将与其他两种方法相同。
Map<Integer, List> convertedMap = converter.streamCollectByStringLength(source, HashMap::new, ArrayList::new);
assertTrue(convertedMap.get(3).contains("Map"));
4. Conclusion
4.总结
In this tutorial, we illustrated how to convert a List<E> into a Map<K, List<E>> with the Java 8 Stream API with custom Suppliers.
在本教程中,我们说明了如何用Java 8 Stream API将List<E>转换为Map<K, List<E>>,并使用自定义Suppliers。
The complete source code with the examples in this tutorial can be found over on GitHub.
本教程中包含示例的完整源代码可以在GitHub上找到over。