java Stream groupingBy:减少到列表的第一个元素
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/34495816/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Stream groupingBy: reducing to first element of list
提问by Fabio B.
I have a List<Valuta>
which can be represented (simplified) JSON-style:
我有一个List<Valuta>
可以表示(简化)JSON 样式的:
[ { codice=EUR, description=Euro, ratio=1 }, { codice=USD, description=Dollars, ratio=1.1 } ]
[ { codice=EUR, description=Euro, ratio=1 }, { codice=USD, description=Dollars, ratio=1.1 } ]
I want to transform that in a Map<String, Valuta>
like this:
我想把它转换成Map<String, Valuta>
这样:
{ EUR={ codice=EUR, description=Euro, ratio=1 }, USD={ codice=USD, description=Dollars, ratio=1.1 }}
{ EUR={ codice=EUR, description=Euro, ratio=1 }, USD={ codice=USD, description=Dollars, ratio=1.1 }}
I wrote this one-liner:
我写了这个单行:
getValute().stream().collect(Collectors.groupingBy(Valuta::getCodice));
but this returns a Map<String, List<Valuta>>
instead of what I need.
但这返回一个Map<String, List<Valuta>>
而不是我需要的。
I suppose mapping()
function would work for me, but don't know how.
我想mapping()
函数对我有用,但不知道如何。
回答by Tunaki
Actually, you need to use Collectors.toMap
here instead of Collectors.groupingBy
:
实际上,您需要在Collectors.toMap
此处使用而不是Collectors.groupingBy
:
Map<String, Valuta> map =
getValute().stream()
.collect(Collectors.toMap(Valuta::getCodice, Function.identity()));
groupingBy
is used to group elements of a Stream based on a grouping function. 2 Stream elements that will have the same result with the grouping function will be collected into a List
by default.
groupingBy
用于根据分组功能对流的元素进行分组。2 与分组功能有相同结果的流元素将List
默认收集到a中。
toMap
will collect the elements into a Map
where the key is the result of applying a given key mapper and the value is the result of applying a value mapper. Note that toMap
, by default, will throw an exception if a duplicate is encountered.
toMap
将元素收集到 a 中Map
,其中键是应用给定键映射器的结果,值是应用值映射器的结果。请注意toMap
,默认情况下,如果遇到重复项,将抛出异常。
回答by Philippe
It's a bit late in the game, but try this:
游戏有点晚了,但试试这个:
Map<String, Valuta> map =
getValute().stream()
.collect(Collectors.groupingBy(Valuta::getCodice,
Collectors.collectingAndThen(
Collectors.toList(),
values -> values.get(0))));
回答by Pshemo
You could use Collectors.toMap(keyMappingFunction, valueMappingFunction)
你可以用 Collectors.toMap(keyMappingFunction, valueMappingFunction)
Map<String, Valuta> map = list
.stream()
.collect(Collectors.toMap(Valuta::getCodice, v -> v));
You can replace v->v
with Function.identity()
if you find it more readable.
如果您发现它更具可读性,您可以替换v->v
为Function.identity()
。
回答by 3ygun
The toMap
version which opts to choose the 1st value on collisions instead of throwing an exception is:
在toMap
其中选择只选择在碰撞第一个值,而不是抛出一个异常的版本:
Collectors.toMap(keyMapper, valueMapper, mergeFunction)
Collectors.toMap(keyMapper, valueMapper, mergeFunction)
Map<String, Valuta> map = list
.stream()
.collect(Collectors.toMap(Valuta::getCodice, v -> v, (v1, v2) -> v1));
回答by Soudipta Dutta
Here are 3 methods.
这里有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
}
回答by Kedar Gokhale
Not completely related to this question but quiet similar case .If you want to group list by multiple params and after that need return object of different type as value then you can try this solution . Hope helps to someone!
与此问题不完全相关,但类似的情况很安静。如果您想按多个参数对列表进行分组,然后需要将不同类型的对象作为值返回,那么您可以尝试此解决方案。希望对某人有帮助!
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;
}