Stream groupingBy:减少到列表的第一个元素
Stream groupingBy: reducing to first element of list
我有一个 List<Valuta>
可以表示(简化)JSON-style:
[ { codice=EUR, description=Euro, ratio=1 }, { codice=USD,
description=Dollars, ratio=1.1 } ]
我想把它转换成 Map<String, Valuta>
这样的:
{ EUR={ codice=EUR, description=Euro, ratio=1 }, USD={ codice=USD,
description=Dollars, ratio=1.1 }}
我写了这个单行:
getValute().stream().collect(Collectors.groupingBy(Valuta::getCodice));
但是这个 returns Map<String, List<Valuta>>
而不是我需要的。
我想 mapping()
功能对我有用,但不知道如何。
你可以使用 Collectors.toMap(keyMappingFunction, valueMappingFunction)
Map<String, Valuta> map = list
.stream()
.collect(Collectors.toMap(Valuta::getCodice, v -> v));
如果您觉得更易读,可以将 v->v
替换为 Function.identity()
。请注意 toMap
,默认情况下,如果遇到重复项将抛出异常。
实际上,你需要在这里使用Collectors.toMap
而不是Collectors.groupingBy
:
Map<String, Valuta> map =
getValute().stream()
.collect(Collectors.toMap(Valuta::getCodice, Function.identity()));
groupingBy
用于根据分组函数对 Stream 的元素进行分组。 2 与分组函数结果相同的 Stream 元素将默认收集到一个List
。
toMap
会将元素收集到 Map
中,其中键是应用给定键映射器的结果,值是应用值映射器的结果。请注意 toMap
,默认情况下,如果遇到重复项将抛出异常。
游戏有点晚了,但试试这个:
Map<String, Valuta> map =
getValute().stream()
.collect(Collectors.groupingBy(Valuta::getCodice,
Collectors.collectingAndThen(
Collectors.toList(),
values -> values.get(0))));
这里有3种方法。
public class Test1 {
static class Foo {
public int id, targetCost, actualCost;
public String ref;
public Foo(int id, String ref, int actualCost, int targetCost) {
this.id = id;
this.targetCost = targetCost;
this.actualCost = actualCost;
this.ref = ref;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public int getTargetCost() {
return targetCost;
}
public void setTargetCost(int targetCost) {
this.targetCost = targetCost;
}
public int getActualCost() {
return actualCost;
}
public void setActualCost(int actualCost) {
this.actualCost = actualCost;
}
public String getRef() {
return ref;
}
public void setRef(String ref) {
this.ref = ref;
}
@Override
public String toString() {
return " [id=" + id + ", targetCost="
+ targetCost + ", " + "actualCost="
+ actualCost + ", ref=" + ref
+ "]";
}
}// foo
public static void main(String[] args) {
List<Foo> list = Arrays.asList(
new Foo(1, "P1", 300, 400), new Foo(2, "P2", 600, 400), new Foo(3, "P3", 30, 20),
new Foo(3, "P3", 70, 20), new Foo(1, "P1", 360, 40), new Foo(4, "P4", 320, 200),
new Foo(4, "P4", 500, 900)
);
// Method 1 :
Map<Integer, List<Foo>> collect = list.stream()
.collect(
Collectors.groupingBy(
Foo::getId,
Collectors.collectingAndThen(
Collectors.toList(),
Function.identity()
)// andthen
)// gr
);
System.out.println(collect);
/*
{
1=[ [id=1, targetCost=400, actualCost=300, ref=P1],
id=1, targetCost=40, actualCost=360, ref=P1]],
2=[ [id=2, targetCost=400, actualCost=600, ref=P2]],
3=[ [id=3, targetCost=20, actualCost=30, ref=P3],
[id=3, targetCost=20, actualCost=70, ref=P3]],
4=[ [id=4, targetCost=200, actualCost=320, ref=P4],
[id=4, targetCost=900, actualCost=500, ref=P4]]
}
*/
// Method 2
Map<Integer, List<Foo>> collect2 = list.stream().collect(
Collectors.groupingBy(
Foo::getId,
Collectors.mapping(
Function.identity(),
Collectors.toList())));
System.out.println(collect2);
/*
{
1=[ [id=1, targetCost=400, actualCost=300, ref=P1],
[id=1, targetCost=40, actualCost=360, ref=P1]],
2=[ [id=2, targetCost=400, actualCost=600, ref=P2]],
3=[ [id=3, targetCost=20, actualCost=30, ref=P3],
[id=3, targetCost=20, actualCost=70, ref=P3]],
4=[ [id=4, targetCost=200, actualCost=320, ref=P4],
[id=4, targetCost=900, actualCost=500, ref=P4]]
}
*/
// Method 3
// If you need to compare something the you can use Compare.comparing
Map<Integer, List<Foo>> collect3 = list
.stream()
.sorted( Comparator.comparing(Foo::getId)
.thenComparing(Foo::getActualCost)
.thenComparing(Foo::getTargetCost) )
.collect(
Collectors.groupingBy(ch -> ch.id)
);
System.out.println(collect3);
/*
{
1=[ [id=1, targetCost=400, actualCost=300, ref=P1],
[id=1, targetCost=40, actualCost=360, ref=P1]],
2=[ [id=2, targetCost=400, actualCost=600, ref=P2]],
3=[ [id=3, targetCost=20, actualCost=30, ref=P3],
[id=3, targetCost=20, actualCost=70, ref=P3]],
4=[ [id=4, targetCost=200, actualCost=320, ref=P4],
[id=4, targetCost=900, actualCost=500, ref=P4]]
}
*/
}// main
}
与这个问题不完全相关,但情况类似。如果您想按多个参数对列表进行分组,然后需要 return 不同类型的对象作为值,那么您可以尝试此解决方案。
希望对某人有所帮助!
public Map<Integer, Map<String, ObjectTypeB>> setObjectTypeB(List<ObjectTypeA> typeAList) {
Map<Integer, Map<String, ObjectTypeB>> map = typeAList.stream()
.collect(groupingBy(ObjectTypeA::getId,
groupingBy(ObjectTypeA::getDate,
collectingAndThen(mapping(this::getObjectTypeB,toList()),values -> values.get(0)))));
return map;
}
public ObjectTypeB getObjectTypeB(ObjectTypeA typeA) {
ObjectTypeB typeB = new ObjectTypeB();
typeB.setUnits(typeA.getUnits());
return typeB;
}
选择在碰撞时选择第一个值而不是抛出异常的 toMap
版本是:
Collectors.toMap(keyMapper, valueMapper, mergeFunction)
例如:
Map<String, Valuta> map = list
.stream()
.collect(Collectors.toMap(Valuta::getCodice, v -> v, (v1, v2) -> v1));
我有一个 List<Valuta>
可以表示(简化)JSON-style:
[ { codice=EUR, description=Euro, ratio=1 }, { codice=USD, description=Dollars, ratio=1.1 } ]
我想把它转换成 Map<String, Valuta>
这样的:
{ EUR={ codice=EUR, description=Euro, ratio=1 }, USD={ codice=USD, description=Dollars, ratio=1.1 }}
我写了这个单行:
getValute().stream().collect(Collectors.groupingBy(Valuta::getCodice));
但是这个 returns Map<String, List<Valuta>>
而不是我需要的。
我想 mapping()
功能对我有用,但不知道如何。
你可以使用 Collectors.toMap(keyMappingFunction, valueMappingFunction)
Map<String, Valuta> map = list
.stream()
.collect(Collectors.toMap(Valuta::getCodice, v -> v));
如果您觉得更易读,可以将 v->v
替换为 Function.identity()
。请注意 toMap
,默认情况下,如果遇到重复项将抛出异常。
实际上,你需要在这里使用Collectors.toMap
而不是Collectors.groupingBy
:
Map<String, Valuta> map =
getValute().stream()
.collect(Collectors.toMap(Valuta::getCodice, Function.identity()));
groupingBy
用于根据分组函数对 Stream 的元素进行分组。 2 与分组函数结果相同的 Stream 元素将默认收集到一个List
。
toMap
会将元素收集到 Map
中,其中键是应用给定键映射器的结果,值是应用值映射器的结果。请注意 toMap
,默认情况下,如果遇到重复项将抛出异常。
游戏有点晚了,但试试这个:
Map<String, Valuta> map =
getValute().stream()
.collect(Collectors.groupingBy(Valuta::getCodice,
Collectors.collectingAndThen(
Collectors.toList(),
values -> values.get(0))));
这里有3种方法。
public class Test1 {
static class Foo {
public int id, targetCost, actualCost;
public String ref;
public Foo(int id, String ref, int actualCost, int targetCost) {
this.id = id;
this.targetCost = targetCost;
this.actualCost = actualCost;
this.ref = ref;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public int getTargetCost() {
return targetCost;
}
public void setTargetCost(int targetCost) {
this.targetCost = targetCost;
}
public int getActualCost() {
return actualCost;
}
public void setActualCost(int actualCost) {
this.actualCost = actualCost;
}
public String getRef() {
return ref;
}
public void setRef(String ref) {
this.ref = ref;
}
@Override
public String toString() {
return " [id=" + id + ", targetCost="
+ targetCost + ", " + "actualCost="
+ actualCost + ", ref=" + ref
+ "]";
}
}// foo
public static void main(String[] args) {
List<Foo> list = Arrays.asList(
new Foo(1, "P1", 300, 400), new Foo(2, "P2", 600, 400), new Foo(3, "P3", 30, 20),
new Foo(3, "P3", 70, 20), new Foo(1, "P1", 360, 40), new Foo(4, "P4", 320, 200),
new Foo(4, "P4", 500, 900)
);
// Method 1 :
Map<Integer, List<Foo>> collect = list.stream()
.collect(
Collectors.groupingBy(
Foo::getId,
Collectors.collectingAndThen(
Collectors.toList(),
Function.identity()
)// andthen
)// gr
);
System.out.println(collect);
/*
{
1=[ [id=1, targetCost=400, actualCost=300, ref=P1],
id=1, targetCost=40, actualCost=360, ref=P1]],
2=[ [id=2, targetCost=400, actualCost=600, ref=P2]],
3=[ [id=3, targetCost=20, actualCost=30, ref=P3],
[id=3, targetCost=20, actualCost=70, ref=P3]],
4=[ [id=4, targetCost=200, actualCost=320, ref=P4],
[id=4, targetCost=900, actualCost=500, ref=P4]]
}
*/
// Method 2
Map<Integer, List<Foo>> collect2 = list.stream().collect(
Collectors.groupingBy(
Foo::getId,
Collectors.mapping(
Function.identity(),
Collectors.toList())));
System.out.println(collect2);
/*
{
1=[ [id=1, targetCost=400, actualCost=300, ref=P1],
[id=1, targetCost=40, actualCost=360, ref=P1]],
2=[ [id=2, targetCost=400, actualCost=600, ref=P2]],
3=[ [id=3, targetCost=20, actualCost=30, ref=P3],
[id=3, targetCost=20, actualCost=70, ref=P3]],
4=[ [id=4, targetCost=200, actualCost=320, ref=P4],
[id=4, targetCost=900, actualCost=500, ref=P4]]
}
*/
// Method 3
// If you need to compare something the you can use Compare.comparing
Map<Integer, List<Foo>> collect3 = list
.stream()
.sorted( Comparator.comparing(Foo::getId)
.thenComparing(Foo::getActualCost)
.thenComparing(Foo::getTargetCost) )
.collect(
Collectors.groupingBy(ch -> ch.id)
);
System.out.println(collect3);
/*
{
1=[ [id=1, targetCost=400, actualCost=300, ref=P1],
[id=1, targetCost=40, actualCost=360, ref=P1]],
2=[ [id=2, targetCost=400, actualCost=600, ref=P2]],
3=[ [id=3, targetCost=20, actualCost=30, ref=P3],
[id=3, targetCost=20, actualCost=70, ref=P3]],
4=[ [id=4, targetCost=200, actualCost=320, ref=P4],
[id=4, targetCost=900, actualCost=500, ref=P4]]
}
*/
}// main
}
与这个问题不完全相关,但情况类似。如果您想按多个参数对列表进行分组,然后需要 return 不同类型的对象作为值,那么您可以尝试此解决方案。 希望对某人有所帮助!
public Map<Integer, Map<String, ObjectTypeB>> setObjectTypeB(List<ObjectTypeA> typeAList) {
Map<Integer, Map<String, ObjectTypeB>> map = typeAList.stream()
.collect(groupingBy(ObjectTypeA::getId,
groupingBy(ObjectTypeA::getDate,
collectingAndThen(mapping(this::getObjectTypeB,toList()),values -> values.get(0)))));
return map;
}
public ObjectTypeB getObjectTypeB(ObjectTypeA typeA) {
ObjectTypeB typeB = new ObjectTypeB();
typeB.setUnits(typeA.getUnits());
return typeB;
}
选择在碰撞时选择第一个值而不是抛出异常的 toMap
版本是:
Collectors.toMap(keyMapper, valueMapper, mergeFunction)
例如:
Map<String, Valuta> map = list
.stream()
.collect(Collectors.toMap(Valuta::getCodice, v -> v, (v1, v2) -> v1));