Java8 Stream流的实战超神操作

 一、描述

Java 8 API添加了一个新的抽象称为流Stream,可以让你以一种声明的方式处理数据。

stream()操作将集合转换成一个流,filter()执行我们自定义的筛选处理,这里是通过lambda表达式筛选出所有偶数,最后我们通过collect()对结果进行封装处理,并通过Collectors.toList()指定其封装成为一个List集合返回。 

下面所有的实例都源自于开发实战总结,集合元素为对象实例。主要通过对象的某些属性进行操作,举一反三即可。下面所有的举例对象均为熟悉的学生对象。

二、单个集合操作

1、排序操作

1)、通过对学生对象中的某个属性进行排序(升序,降序)操作。

2)、注意:使用stream流排序时不改变原集合!但是下面代码提供了一种改变排序后对象的集合操作方式。

public class DTest {

    @Data
    @AllArgsConstructor
    class User {
        String name;
        String sex;
        int age;
    }

    @Test
    public void dTest() {
        // 这是一个自定义集合
        ArrayList users = Lists.newArrayList();
        users.add(new User("小明", "男", 12));
        users.add(new User("小明", "女", 10));
        users.add(new User("小红", "女", 14));
        users.add(new User("小红", "男", 10));
        // 根据年龄升序
        List collect1 = users.stream().sorted(Comparator.comparing(User::getAge)).collect(Collectors.toList());
        System.out.println(collect1);
        //[User(name=小明, sex=女, age=10), User(name=小红, sex=男, age=10), User(name=小明, sex=男, age=12), User(name=小红, sex=女, age=14)]
        
        // 根据年龄降序
        List collect2 = users.stream().sorted(Comparator.comparing(User::getAge).reversed()).collect(Collectors.toList());
        System.out.println(collect2);
        //[User(name=小红, sex=女, age=14), User(name=小明, sex=男, age=12), User(name=小明, sex=女, age=10), User(name=小红, sex=男, age=10)]

        // 按年龄,姓名升序排序
        List collect3 = users.stream().sorted(Comparator.comparing(User::getAge).thenComparing(User::getName)).collect(Collectors.toList());
        System.out.println(collect3);
        //[User(name=小明, sex=女, age=10), User(name=小红, sex=男, age=10), User(name=小明, sex=男, age=12), User(name=小红, sex=女, age=14)]

        // 在List集合内部排序,改变原来的集合元素顺序
        // 按年龄倒序
        collect3.sort(Comparator.comparing(User::getAge).reversed());
        System.out.println(collect3);
        //[User(name=小红, sex=女, age=14), User(name=小明, sex=男, age=12), User(name=小明, sex=女, age=10), User(name=小红, sex=男, age=10)]

    }
}

2、分组操作

1)、提供对学生对象的某个属性(例如姓名)进行分组的操作,分组后为Map对象。

2)、对于分组后的结果,可以对结果集合进行表达式计算,例如计算平均分等。

public class DTest {

    @Data
    @AllArgsConstructor
    class User{
        String name;
        String sex;
        int age;
    }

    @Test
    public void dTest(){
        // 这是一个自定义集合
        ArrayList users = Lists.newArrayList();
        users.add(new User("小明", "男", 12));
        users.add(new User("小明", "女", 10));
        users.add(new User("小红", "女", 14));
        users.add(new User("小红", "女", 12));
        users.add(new User("小红", "男", 10));


        // 分组结果
        Map> groupBy = users.stream().collect(Collectors.groupingBy(User::getName));
        System.out.println(groupBy);
        //{小明=[DTest.User(name=小明, sex=男, age=12), DTest.User(name=小明, sex=女, age=10)],小红=[DTest.User(name=小红, sex=女, age=14), DTest.User(name=小红, sex=女, age=12), DTest.User(name=小红, sex=男, age=10)]}

        // 分组平均值结果
        Map avgGroup = users.stream().collect(Collectors.groupingBy(User::getName, Collectors.averagingInt(User::getAge)));
        System.out.println(avgGroup);
        // {小明=11.0, 小红=12.0}

        // 分组总数值结果
        Map sumGroup = users.stream().collect(Collectors.groupingBy(User::getName, Collectors.summingInt(User::getAge)));
        System.out.println(sumGroup);
        // {小明=22, 小红=36}
        
    }
}

3、List集合转Map

1)、提供对学生对象某个属性进行提取,转换为Map对象,Map对象可能存在Key值重复导致报错,所以下方个例举例了,冲突时保留策略。

2)、提供对集合的转Map时,同时可以抽取部分属性自定义Map的 value 值。

public class DTest {

    @Data
    @AllArgsConstructor
    class User{
        String name;
        String sex;
        int age;
    }

    @Test
    public void dTest(){
        // 这是一个自定义集合
        ArrayList users = Lists.newArrayList();
        users.add(new User("小明", "男", 12));
        users.add(new User("小明", "女", 10));
        users.add(new User("小红", "女", 14));
        users.add(new User("小红", "女", 12));
        users.add(new User("小红", "男", 10));
      
        // List转Map, 遇到相同的key, k值则保留第一次遇到的值
        Map toMapK1 = users.stream().collect(Collectors.toMap(User::getName, a -> a, (k1,k2) -> k1));
        System.out.println(toMapK1);
        // {小明=DTest.User(name=小明, sex=男, age=12), 小红=DTest.User(name=小红, sex=女, age=14)}

        // k值保留第二次遇见的数据
        Map toMapK2 = users.stream().collect(Collectors.toMap(User::getName, a -> a, (k1,k2) -> k2));
        System.out.println(toMapK2);
        //{小明=DTest.User(name=小明, sex=女, age=10), 小红=DTest.User(name=小红, sex=男, age=10)}

        // 取对象中的两个属性组成Map
        Map collect = users.stream().collect(Collectors.toMap(User::getName, a -> a.getSex(), (k1, k2) -> k2));
        System.out.println(collect);
        //{小明=女, 小红=男}


    }
}

4、去重演示

开发过程中经常需要需要去重的情况,其中主要比较棘手的是List集合中的元素,根据元素的某个属性或多个属性去重!以下代码演示!

1)、对象单个属性去重

举例了根据学生对象中的某个属性值去重,默认保留第一次遇到的。所以如果有保留的某些需求。可以先对集合进行排序。

public class DTest {

    @Data
    @AllArgsConstructor
    class User{
        String name;
        String sex;
        int age;
    }

    @Test
    public void dTest(){
        // 这是一个自定义集合
        ArrayList users = Lists.newArrayList();
        users.add(new User("小明", "男", 12));
        users.add(new User("小明", "女", 10));
        users.add(new User("小红", "女", 14));
        users.add(new User("小红", "女", 12));
        users.add(new User("小红", "男", 10));
        // 根据User.name属性值去重
        ArrayList userList = users.stream().collect(Collectors.collectingAndThen(
                Collectors.toCollection(() -> new TreeSet<>(Comparator.comparing(User::getName))), ArrayList::new));
        
        // 按顺序保留了第一次遇到name
        System.out.println(userList);
        //[DTest.User(name=小明, sex=男, age=12), DTest.User(name=小红, sex=女, age=14)]

    }
}

2)、对个多个属性组合去重

举例了根据学生对象中的多个属性值组合来去重,默认保留第一次遇到的。所以如果有保留的某些需求。可以先对集合进行排序。

public class DTest {

    @Data
    @AllArgsConstructor
    class User{
        String name;
        String sex;
        int age;
    }

    @Test
    public void dTest(){
        // 这是一个自定义集合
        ArrayList users = Lists.newArrayList();
        users.add(new User("小明", "男", 12));
        users.add(new User("小明", "女", 10));
        users.add(new User("小红", "女", 14));
        users.add(new User("小红", "女", 12));
        users.add(new User("小红", "男", 10));
        // 根据User.name和User.sex 两个属性值组合去重
        List userList= users.stream().collect(
                Collectors.collectingAndThen(Collectors.toCollection(() ->
                        new TreeSet<>(Comparator.comparing(o -> o.getName() + ";" + o.getSex()))
                ), ArrayList::new));

        // 按顺序保留了第一次遇到name+sex,结果去掉了12岁的小红
        System.out.println(userList);
        //[DTest.User(name=小明, sex=女, age=10), DTest.User(name=小明, sex=男, age=12), DTest.User(name=小红, sex=女, age=14), DTest.User(name=小红, sex=男, age=10)]
    }
}

5、Map元素遍历

1)、通过map()方法遍历返回集合中对象某个属性的集合。

2)、通过map()方法遍历,对遍历对象进行操作,然后返回自定义的集合。

public class DTest {

    @Data
    @AllArgsConstructor
    class User{
        String name;
        String sex;
        int age;
    }

    @Test
    public void dTest(){
        // 这是一个自定义集合
        ArrayList users = Lists.newArrayList();
        users.add(new User("小明", "男", 12));
        users.add(new User("小明", "女", 10));

        // 获取年龄数组
        List collect = users.stream().map(User::getAge).collect(Collectors.toList());
        // 按顺序获取年龄
        System.out.println(collect);
        // [12, 10]

        // 对元素的属性进行操作
        List userList = users.stream().map(e -> {
            e.setName("小红");
            return e;
        }).collect(Collectors.toList());
        // 全部改名
        System.out.println(userList);
        // [DTest.User(name=小红, sex=男, age=12), DTest.User(name=小红, sex=女, age=10)]


    }
}

6、Filter 根据条件过滤元素

1)、学生对象单个属性自定义规则筛选,返回集合。

2)、学生对象属性自定义规则筛选,对筛选结果进行自定义截取,返回最终的集合。

3)、学生对象多个属性自定义规则筛选,返回集合。

public class DTest {

    @Data
    @AllArgsConstructor
    class User{
        String name;
        String sex;
        int age;
    }

    @Test
    public void dTest() {
        // 这是一个自定义集合
        ArrayList users = Lists.newArrayList();
        users.add(new User("小明", "男", 12));
        users.add(new User("小明", "男", 10));
        users.add(new User("小红", "女", 14));

        // 单属性筛选: 姓名为小明的人
        List user1 = users.stream().filter(e -> e.getName().equals("小明")).collect(Collectors.toList());
        System.out.println(user1);
        // [DTest.User(name=小明, sex=男, age=12), DTest.User(name=小明, sex=男, age=10)]

        /**
         *  筛选结果扩展:
         *      limit(n): 返回另一个不超过给定大小的流,取前几个;超过集合大小则返回所有;
         *      skip(n) : 返回丢弃前n个元素的流,如果流中的元素少于n个,则返回空流;
         */
        List user2 = users.stream().filter(e -> e.getName().equals("小明")).limit(1).collect(Collectors.toList());
        System.out.println(user2);
        // [DTest.User(name=小明, sex=男, age=12)]

        List user3 = users.stream().filter(e -> e.getName().equals("小明")).skip(1).collect(Collectors.toList());
        System.out.println(user3);
        // [DTest.User(name=小明, sex=男, age=10)]


        /**
         * 多属性筛选: 姓名为小明,年龄=10的人
         */
        List user4 = users.stream().filter(e -> {
            if (e.getName().equals("小明") && e.getAge() == 10) {
                return true;
            }
            return false;
        }).collect(Collectors.toList());
        System.out.println(user4);
        // [DTest.User(name=小明, sex=男, age=10)]
    }
}

7、reduce 聚合方法

Stream.reduce() 是 Stream 的一个聚合方法,它可以把一个Stream 的所有元素按照聚合函数聚合成一个结果。常用可以进行累加累减少!

 案例主要是举例进行了累加,累减,字符串拼接操作,具体的通用规则也可以自定义。

public class DTest {

    @Data
    @AllArgsConstructor
    class User{
        String name;
        String sex;
        int age;
    }

    @Test
    public void dTest(){
        // 这是一个自定义集合
        ArrayList users = Lists.newArrayList();
        users.add(new User("小明", "男", 12));
        users.add(new User("小明", "女", 10));
        users.add(new User("小红", "女", 14));

        // 从0开始累加:0为设置的初始值
        Integer collect1 = users.stream().map(User::getAge).reduce(0, (a, b) -> a + b);
        System.out.println(collect1);    // 36

        // 从设置的初始值12进行累减
        Integer collect2 = users.stream().map(User::getAge).reduce(12, (a, b) -> a - b);
        System.out.println(collect2);    // -24

        // 建名字进行字符串拼接
        String collect3 = users.stream().map(User::getName).reduce("姓名集:", (a, b) -> a + b);
        System.out.println(collect3);    // 姓名集:小明小明小红

    }
}

二、多个集合操作

集合之间的关系基本如下。一图秒解,下面的实例基本以元素对象的某个或多个属性进行讲解,基本来源于实战总结。

交集:A & B,即A与B ( x x ( ) x x )
并集:A | B, 即A或B ( ( ) )
差集:A - B, 即A减B ( ( x x ) x x )
补集:A ^ B,即A异B ( ( x x )   )

Java8 Stream流的实战超神操作_第1张图片

1、并集

并集的核心解题思路是,首先是将两个集合合并,然后对合并结果去重,上面教程元素单属性去重,元素自定义多属性去重,可往上查看。

1)、基本类型元素去重并集

1. 使用Stream.concat()方法将 list1 和 list2 合并成一个Stream对象。
2. 使用Stream.distinct() 方法去重,保留不同的学生对象。
3. 使用Collectors.toList()方法将Stream对象转换成List<>对象,存储到resultList中。

注意:Stream.distinct()方法去重自定义对象,例如Student等,Student类需要实现equals()和hashCode()方法,否则不能正确去重。

@Test
public void dTest(){
    // 这是一个自定义集合list1
    ArrayList list1 = Lists.newArrayList();
    list1.add(22);
    list1.add(23);
    // 这是一个自定义集合list2
    ArrayList list2 = Lists.newArrayList();
    list1.add(23);
    list1.add(24);
    List resultList = Stream.concat(list1.stream(), list2.stream())
            .distinct()
            .collect(Collectors.toList());
    System.out.println(ArrayUtil.toString(resultList));
    // 运行结果:[22, 23, 24]
}

2)、自定义对象属性去重并集

如下是举例了根据元素指定的单个属性去重,如果根据多个属性去重,则看回上面的教程查看。

class DTest {
    @Data
    @AllArgsConstructor
    class User{
        String name;
        String sex;
        int age;
    }

    @Test
    public void dTest(){
        // 这是一个自定义集合
        ArrayList users1 = Lists.newArrayList();
        users1.add(new User("小明", "男", 12));
        users1.add(new User("小华", "男", 10));
        users1.add(new User("小红", "女", 14));

        // 这是一个自定义集合
        ArrayList users2 = Lists.newArrayList();
        users2.add(new User("小明", "男", 12));
        users2.add(new User("小华", "男", 16));
        users2.add(new User("小青", "女", 16));
        /**
         * 使用TreeSet去重,最后将结果转换为ArrayList集合并返回。
         * 其中,Comparator.comparing()方法用于指定按照Age进行排序。根据指定的排序字段去重
         */
        List resultList = Stream.concat(users1.stream(), users2.stream())
                .collect(Collectors.collectingAndThen(
                        Collectors.toCollection(() -> new TreeSet<>(Comparator.comparing(User::getAge))),
                        ArrayList::new));
        resultList.forEach(System.out::println);
        /*
        打印结果:
            DTest.User(name=小华, sex=男, age=10)
            DTest.User(name=小明, sex=男, age=12)
            DTest.User(name=小红, sex=女, age=14)
            DTest.User(name=小华, sex=男, age=16)
         */
    }

}

2、交集

 1)、元素对象所有属性匹配交集

class DTest {
    @Data
    @AllArgsConstructor
    class User{
        String name;
        String sex;
        int age;
    }

    @Test
    public void dTest(){
        // 这是一个自定义集合
        ArrayList users1 = Lists.newArrayList();
        users1.add(new User("小明", "男", 12));
        users1.add(new User("小华", "男", 10));
        users1.add(new User("小红", "女", 14));

        // 这是一个自定义集合
        ArrayList users2 = Lists.newArrayList();
        users2.add(new User("小明", "男", 12));
        users2.add(new User("小华", "男", 16));
        users2.add(new User("小青", "女", 16));
        /**
         * 使用filter条件筛选(选取两者集合互相包含的元素),最后将结果转换为ArrayList集合并返回。
         */
        List resultList = users1.stream()
                .filter(users2::contains)
                .collect(Collectors.toList());
        resultList.forEach(System.out::println);
        /*
          打印结果:
            DTest.User(name=小明, sex=男, age=12)
         */
    }
    
}

2)、元素对象指定属性交集

在filter过滤器中,使用自定义匹配规则,两个集合中被规则匹配中的元素则为交集。

class DTest {
    @Data
    @AllArgsConstructor
    class User{
        String name;
        String sex;
        int age;
    }

    @Test
    public void dTest(){
        // 这是一个自定义集合
        ArrayList users1 = Lists.newArrayList();
        users1.add(new User("小明", "男", 12));
        users1.add(new User("小华", "男", 10));
        users1.add(new User("小红", "女", 14));

        // 这是一个自定义集合
        ArrayList users2 = Lists.newArrayList();
        users2.add(new User("小明", "男", 12));
        users2.add(new User("小华", "男", 16));
        users2.add(new User("小青", "女", 16));
        /**
         * 使用filter条件筛选(选取两者集合指定的匹配规则),最后将结果转换为ArrayList集合并返回。
         */
        List resultList = users1.stream()
                .filter(s1 -> users2.stream()
                        .anyMatch(s2 -> s1.getName().equals(s2.getName()) && s1.getSex().equals(s2.getSex())))
                .collect(Collectors.toList());
        resultList.forEach(System.out::println);
        /*
          打印结果:
            DTest.User(name=小明, sex=男, age=12)
            DTest.User(name=小华, sex=男, age=10)
         */
    }

}

3、差集

 在filter过滤器中,使用自定义匹配规则,排除两个集合中被规则匹配中的元素则为差集。

class DTest {
    @Data
    @AllArgsConstructor
    class User{
        String name;
        String sex;
        int age;
    }

    @Test
    public void dTest(){
        // 这是一个自定义集合
        ArrayList users1 = Lists.newArrayList();
        users1.add(new User("小明", "男", 12));
        users1.add(new User("小华", "男", 10));
        users1.add(new User("小红", "女", 14));

        // 这是一个自定义集合
        ArrayList users2 = Lists.newArrayList();
        users2.add(new User("小明", "男", 12));
        users2.add(new User("小华", "男", 16));
        users2.add(new User("小青", "女", 16));
        /**
         * 使用filter条件筛选(排除两者集合指定的匹配规则选中的元素),最后将结果转换为ArrayList集合并返回。
         */
        List resultList = users1.stream()
                .filter(s1 -> users2.stream()
                        .noneMatch(s2 -> s1.getName().equals(s2.getName()) && s1.getSex().equals(s2.getSex())))
                .collect(Collectors.toList());
        resultList.forEach(System.out::println);
        /*
          打印结果:
            DTest.User(name=小红, sex=女, age=14)
         */
    }

}

你可能感兴趣的:(java8,java8去重,java8,stream流,java8集合交集,并集,差集,stream流去重,stream流分组,转map)