按一个字段分组,并计算Java8中的非空字段
我尝试使用Java8特性 我有一节课按一个字段分组,并计算Java8中的非空字段,java,collections,java-8,java-stream,Java,Collections,Java 8,Java Stream,我尝试使用Java8特性 我有一节课 @Data @NoArgsConstructor @AllArgsConstructor class User { private String pId; private String uId; private String price; } 我有一个列表,我尝试按pId分组,并计算非空的uId和price。例如: List<User> list = Arrays.asList( new User ("
@Data
@NoArgsConstructor
@AllArgsConstructor
class User {
private String pId;
private String uId;
private String price;
}
我有一个列表,我尝试按pId
分组,并计算非空的uId
和price
。例如:
List<User> list =
Arrays.asList(
new User ("p1", "u1", null),
new User ("p1", "u2", "a"),
new User ("p2", null, "b"),
new User ("p2", null, "c"),
new User ("p3", "u4", "d")
);
我试着跟在后面
Map<String, Map<Object, Long>> collect =
list.stream()
.collect(
Collectors.groupingBy(
User ::getPId,
Collectors.groupingBy(f -> f.getUId(), Collectors.counting())));
因为我是java新手,所以我要努力完成它,我尽了最大的努力。可以删除空字段并计数吗?您可以尝试下面的解决方案吗?它将提供正确的输出:
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;
public class Main {
public static void main(String[] args) {
List<User> list =
Arrays.asList(
new User ("p1", "u1", null),
new User ("p1", "u2", "a"),
new User ("p2", null, "b"),
new User ("p2", null, "c"),
new User ("p3", "u4", "d")
);
Set<Stat> set = list.stream().map(s -> new Stat(s.getPId(), 0, 0)).collect(Collectors.toSet());
set = set.stream().map(s -> updateCounts(list, s.getPId())).collect(Collectors.toSet());
System.out.println(set);
}
public static Stat updateCounts(List<User> list, String pId) {
Long uCount = list.stream().filter(s -> pId.equals(s.getPId()) && Objects.nonNull(s.getUId()))
.collect(Collectors.counting());
Long priceCount = list.stream().filter(s -> pId.equals(s.getPId()) && Objects.nonNull(s.getPrice()))
.collect(Collectors.counting());
return new Stat(pId, Integer.valueOf(uCount+""), Integer.valueOf(priceCount+""));
}
}
导入java.util.array;
导入java.util.List;
导入java.util.Objects;
导入java.util.Set;
导入java.util.stream.collector;
公共班机{
公共静态void main(字符串[]args){
列表=
Arrays.asList(
新用户(“p1”、“u1”、null),
新用户(“p1”、“u2”、“a”),
新用户(“p2”,空,“b”),
新用户(“p2”,空,“c”),
新用户(“p3”、“u4”、“d”)
);
Set Set=list.stream().map(s->newstat(s.getPId(),0,0)).collect(Collectors.toSet());
set=set.stream().map(s->updateCounts(list,s.getPId()).collect(Collectors.toSet());
系统输出打印项次(套);
}
公共静态统计更新计数(列表、字符串pId){
Long uCount=list.stream().filter(s->pId.equals(s.getPId())&&Objects.nonNull(s.getUId())
.collect(collector.counting());
Long priceCount=list.stream().filter(s->pId.equals(s.getPId())&&Objects.nonNull(s.getPrice())
.collect(collector.counting());
返回新的Stat(pId,Integer.valueOf(uCount+“”),Integer.valueOf(priceCount+“”);
}
}
我建议以下解决方案。它对每个组使用缩减操作,并且需要在Stat
类中添加一些内容,以便代码看起来整洁一些
@数据
@诺尔格构装师
@AllArgsConstructor
类统计{
私有字符串pId;
私有整数uCount;
私有整数价格计数;
公共静态Stat init(){
返回新的Stat(null,Integer.valueOf(0),Integer.valueOf(0));
}
公共价值(统计){
this.uCount+=(s.uCount!=null)?s.uCount:0;
this.priceCount+=(s.priceCount!=null)?s.priceCount:0;
}
公共静态统计mapToStat(用户){
stats=newstat(user.getPId(),0,0);
if(user.getUid()!=null){
s、 uCount++;
}
if(user.getPrice()!=null){
s、 priceCount++;
}
}
}
现在,流操作:
return list.stream()//返回的列表
.collect(Collectors.groupingBy(User::getPId,Collectors.reduction)(new Stat(),Stat::mapToStat,(s1,s2)->{
//您也可以将下面的部分移动到Stat中的方法
Stat s=Stat.init();
s、 setPId(s1.getPId());
s、 incValue(s1);
s、 inc值(s2);
如果(s1.getPId()==null){
s、 setPId(s2.getPId());
}
返回s;
})))
.values()
.stream()
.collect(Collectors.toList());
我将把优化/清理部分留给您:-)
此外,请查看此问题/答案。这也是一个非常相似但略有不同的场景。
Java 12+解决方案 通过使用
teeing
采集器和filtering
可以执行以下操作:
Map<String, Detail> result = list.stream()
.collect(Collectors.groupingBy(User::getpId, Collectors.teeing(
Collectors.filtering(u -> u.getuId() != null, Collectors.counting()),
Collectors.filtering(u -> u.getPrice() != null, Collectors.counting()),
(uCount, priceCount) -> new Detail(uCount, priceCount)
)));
输出:
{
p1=详细信息{uCount=2,priceCount=1},
p2=详细信息{uCount=0,priceCount=2},
p3=详细信息{uCount=1,priceCount=1}
}
您可以使用
减少操作:
导入静态java.util.stream.collector.*;
函数getStatFromUser=
u->new Stat(u.getpId(),
u、 getuId()==null?0:1,
u、 getPrice()==null?0:1);
二进制运算符addStats=
(o1,o2)->o1(标识元素)的第一个元素pId的新Stat(//Imp to getpId from o2)将为“”
o2.getpId(),
o1.getuCount()+o2.getuCount(),
o1.getPriceCount()+o2.getPriceCount());
映射结果=
list.stream().collect(
groupingBy(用户::getpId,
减少(
新统计数据(“”),
u->statFromUser.apply(u),
(o1,o2)->addStats.apply(o1,o2));
对于讨论中的测试用例,输出应该如下所示:
{
p1={pId=p1,uCount=2,priceCount=1},
p2={pId=p2,uCount=0,priceCount=2},
p3={pId=p3,uCount=1,priceCount=1}
}
要获得预期的输出,可以调用collecting,然后在groupingBy
上调用
对象结果=
list.stream()
.收集(
收集然后(
groupingBy(用户::getpId,
减少(新产量(“”),
u->outputFromUser.apply(u),
(o1,o2)->添加组。应用(o1,o2)),
m->m.values());
上述构造的输出为:
[
输出{pId=p1,uCount=2,priceCount=1},
输出{pId=p2,uCount=0,priceCount=2},
输出{pId=p3,uCount=1,priceCount=1}
]
编辑1:
另一种解决方案是使用toMap
:
对象结果=
list.stream()
.收集(
收集然后(
toMap(用户::getpId,
u->outputFromUser.apply(u),
(o1,o2)->添加组。应用(o1,o2)),
m->m.values());
做这样的工作可能会干净得多。@Naman是的。上面的减少
部分涉及不必要的Stat
对象创建为什么不在问题中使用Stat
类而不是import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;
public class Main {
public static void main(String[] args) {
List<User> list =
Arrays.asList(
new User ("p1", "u1", null),
new User ("p1", "u2", "a"),
new User ("p2", null, "b"),
new User ("p2", null, "c"),
new User ("p3", "u4", "d")
);
Set<Stat> set = list.stream().map(s -> new Stat(s.getPId(), 0, 0)).collect(Collectors.toSet());
set = set.stream().map(s -> updateCounts(list, s.getPId())).collect(Collectors.toSet());
System.out.println(set);
}
public static Stat updateCounts(List<User> list, String pId) {
Long uCount = list.stream().filter(s -> pId.equals(s.getPId()) && Objects.nonNull(s.getUId()))
.collect(Collectors.counting());
Long priceCount = list.stream().filter(s -> pId.equals(s.getPId()) && Objects.nonNull(s.getPrice()))
.collect(Collectors.counting());
return new Stat(pId, Integer.valueOf(uCount+""), Integer.valueOf(priceCount+""));
}
}
Map<String, Detail> result = list.stream()
.collect(Collectors.groupingBy(User::getpId, Collectors.teeing(
Collectors.filtering(u -> u.getuId() != null, Collectors.counting()),
Collectors.filtering(u -> u.getPrice() != null, Collectors.counting()),
(uCount, priceCount) -> new Detail(uCount, priceCount)
)));
class Detail{
private long uCount;
private long priceCount;
}