为了账号安全,请及时绑定邮箱和手机立即绑定

JAVA8用stream解决分组的问题

JAVA8用stream解决分组的问题

米琪卡哇伊 2019-03-01 10:51:03
User user =new User();user.setAge=1;user.setCount =1;User user1 =new User();user1.setAge =1;user1.setCount=1......list.add(user);list.add(user1)....我现在有一个集合list,里面的所有user的count都是1,age有很多多,我现在想要做的是吧list中所有age相同的user,合并成1个,count是这些age相同的user的个数,最后在吧这些放到新的list中。因为最近接触了java8的stream(),好像有一个groupby的方法,list.stream.collect(xxxx),提问:用stream怎么写?有什么好的关于stream的资料吗??
查看完整描述

2 回答

?
慕丝7291255

TA贡献1859条经验 获得超6个赞

GroupBy是个Collector,它是用来进行Stream上的collect操作的。Collect是一个Mutable Reduction。

所谓reduction,相当于把集合里的每一个元素依次带入一个函数,最终得到一个值。
比如求一组int的和,可以用reduction写作。

int sum = numbers.stream().reduce(0, (sum,n) -> sum+n);

其中0是初始和,函数(sum,n)->sum+n 对每个整数调用,最终得到所有数的和。

而所谓Mutable Reduction。是指最终产生的值是个可变的对象,比如list。

回到GroupBy, 它会根据条件把数据产生为Map<条件,List<数据>>的形式。
所以如果用GroupBy做到你想要达到的效果,可以这样写:

List<User> users = Arrays.asList(newUser(20), newUser(35), newUser(20));

List<User> groupedUsers = users.stream()
        .collect(Collectors.groupingBy(User::getAge)) //Map {20: [user{count:1, age:20}, user{count:1, age:20}]}, {35: [user{count:1, age:35}]
        .entrySet() //Map变为entry列表,方便继续Stream操作
        .stream().map(
            (entry) -> new User(entry.getValue().size(), entry.getKey()))
            //根据Entry产生新的User,list的长度是新的Count,key值是age
        .sorted(Comparator.comparingInt(User::getAge)) //排序是为了方便后面验证结果
        .collect(Collectors.toList()); //从Stream变回list

assertEquals(2, groupedUsers.size());
assertEquals(20, groupedUsers.get(0).getAge());
assertEquals(2, groupedUsers.get(0).getCount());
assertEquals(35, groupedUsers.get(1).getAge());
assertEquals(1, groupedUsers.get(1).getCount());

当然也可以直接写collect的逻辑,不使用GroupBy产生的Map。

List<User> groupedUsers = users.stream().collect(
    ArrayList<User>::new,  //初始的list
    (list, user)-> {  //对每个User调用的函数
        Optional<User> ageAdded = list.stream()
                .filter(u1 -> u1.getAge() == user.getAge())
                .findAny();  //查找是否已经加入list了
        if ( ageAdded.isPresent() ) {  //如果这个age已经在list了,修改user的count值
            User counted = ageAdded.get();
            counted.setCount(counted.getCount()+1);
        } else { //加入新的user
            list.add(new User(user.getCount(), user.getAge()));
        }
    },
    ArrayList<User>::addAll //合并各小段user list的函数,不并行处理Stream的话不会用到
);

可以看到因为要找list里已经加入的user,所以这样写也并不简单,而且可能比map更加低效。

查看完整回答
反对 回复 2019-03-01
?
明月笑刀无情

TA贡献1828条经验 获得超4个赞

用下面思路:

stream是在可遍历对象上应用。如果需要分组就用groupingBy。

查看完整回答
反对 回复 2019-03-01
  • 2 回答
  • 0 关注
  • 2213 浏览

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信