章
目
录
学习如何将Stream转换为Map,即使用Collectors.toMap()和Collectors.groupingBy()方法将Stream的项收集到Map中。
1.注意IllegalStateException
请注意,首先我们要知道Stream元素是否具有转Map后键字段的相同的情况,如果有的话,并且我们使用Collectors.toMap()方法,代码将抛出IllegalStateException。类似如下:
Duplicate key 3 (attempted merging values Item[id=3, name=Item3] and Item[id=3, name=Item3])
at java.base/java.util.stream.Collectors.duplicateKeyException(Collectors.java:135)
2.将Stream转换为Map(无重复键)
在这种情况下,我们必须确保Stream中的每个键都是唯一的。处理完成后,我们将获得Map<K,V>,其中指定的键K仅与单个值V相关联。
为了演示目的,我们使用包含3个项目的Stream:
Stream<Item> stream = Stream.of(
new Item(1, "Item1"),
new Item(2, "Item2"),
new Item(3, "Item3")
);
我们可以将上述Item实例的Stream转换为两种方式的Map<Long, String>:
- Map键- Item ID
Map值- Item Name。
Map<Long, String> mapWithValue = stream.collect(Collectors.toMap(Item::id, Item::name));
// {1=Item1, 2=Item2, 3=Item3}
在下一个示例中,我们使用Function.identity()将对象本身作为Map值来转换为Map<Long, Item>。
Map<Long, Item> map = stream.collect(Collectors.toMap(Item::id, Function.identity()));
// {1=Item[id=1, name=Item1], 2=Item[id=2, name=Item2], 3=Item[id=3, name=Item3]}
3. 将Stream转换为Map(有重复键)
如果流中有重复键的元素,有两种可能的方法来处理它:
- 将所有值收集到List中并将它们与键关联起来
- 仅选择一个值,并丢弃同一键的所有其他值
为了演示目的,我们使用包含五个元素的Stream,其中三个项目具有重复键’3’。
Stream<Item> streamWithDuplicates = Stream.of(
new Item(1, "Item1"),
new Item(2, "Item2"),
new Item(3, "Item3-1"),
new Item(3, "Item3-2"),
new Item(3, "Item3-3")
);
3.1将Stream收集到Map的List中
如果我们想将所有键的值存储在List中,我们可以使用Collectors.groupingBy()来收集元素,形成Map<key, List>的格式。在下面的示例中,我们将项目收集到Map的List中,即Map<K, List<Item>>。
Map<Long, List<Item>> mapWithList = streamWithDuplicates.collect(Collectors.groupingBy(Item::id));
程序输出:
{
1=[Item[id=1, name=Item1]],
2=[Item[id=2, name=Item2]],
3=[
Item[id=3, name=Item3-1],
Item[id=3, name=Item3-2],
Item[id=3, name=Item3-3]
]
}
我们可以将该Stream转换为List包含Item名称的Map,即Map<K, List<String>>。
Map<Long, List<String>> mapWithGroupedValues = streamWithDuplicates
.collect(
Collectors.groupingBy(Item::id,
Collectors.mapping(Item::name, Collectors.toList())));
程序输出:
{
1=[Item1],
2=[Item2],
3=[Item3-1, Item3-2, Item3-3]
}
3.2. 将Stream收集到Map中并丢弃重复值
另一种方法是仅从多个值中选择一个值,并丢弃相同键的其他值。
在我们的示例中,我们可能决定选择Map键的最新名称,即item3-3,并丢弃其他值,即item3-1和item3-2。
以下代码使用第三个参数mergeFunction,当遇到键上的冲突值时,它选择必须与键关联的值。
Map<Long, Item> mapWithGrouping = streamWithDuplicates
.collect(Collectors.toMap(Item::id, Function.identity(), (oldValue, newValue) -> newValue));
程序输出:
{
1=Item[id=1, name=Item1],
2=Item[id=2, name=Item2],
3=Item[id=3, name=Item3-3]
}
4.维持插入顺序或按键排序
有时,我们可能希望维护将键值对插入Map时的顺序。 LinkedHashMap维护这样的插入顺序,因此我们可以使用它来收集Stream项。
LinkedHashMap<Long, String> mapWithValueInInsertionOrder = stream
.collect(Collectors.toMap(
Item::id,
Item::name,
(o, n) -> n,
LinkedHashMap::new));
同样,如果我们想应用并维护Map键的排序顺序,我们可以使用TreeMap。
TreeMap<Long, String> mapwithSortedKeys = stream
.collect(Collectors.toMap(
Item::id,
Item::name,
(o, n) -> n,
TreeMap::new));
5. 结论
本Java Stream教程讨论了将stream项收集到Map或List的多种方法。 我们看到了每种方法的示例及其输出,以便更好地了解每种方法对stream项的作用。