JAVA8 Stream流,集合操作利器(筛选、归约、分组、聚合)

序:

我们在做后端开发的时候,绝大多数情况下都是在操作集合,对集合的增删改,然后返回到前端渲染。
一般情况,我们将集合视为一个可伸缩的数组,通过add、set、remove 等操作对集合进行增改删等。很多时候,我们需要对集合中的每一个数据对象进行操作,我们便会用for循环,有没有其他的快捷简单的优雅的方式呢?stream流就是解决集合的遍历的。
eg:

  • 从员工集合中筛选出salary大于8000的员工,并放置到新的集合里。
  • 统计员工的最高薪资、平均薪资、薪资之和。
  • 将员工按薪资从高到低排序,同样薪资者年龄小者在前。
  • 将员工按性别分类,将员工按性别和地区分类,将员工按薪资是否高于8000分为两部分。
    这些功能,用传统的迭代处理也不是很难,但代码就显得冗余了,跟Stream相比高下立判。
    stream流的api相关的脑图如图:
    在这里插入图片描述

1 Stream概述

Java 8 是一个非常成功的版本,这个版本新增的Stream,配合同版本出现的 Lambda ,给我们操作集合(Collection)提供了极大的便利。
那么什么是Stream?Stream将要处理的元素集合看作一种流,在流的过程中,借助Stream API对流中的元素进行操作,比如:筛选、排序、聚合等。
Stream可以由数组或集合创建,对流的操作分为两种:

  1. 中间操作:每次返回一个新的流,例如我们对它进行过滤之后,会返回一个过滤之后的流,然后可以继续对过滤之后的这个流进行排序之类的操作。
  2. 终端操作:每个流只能进行一次终端操作,终端操作结束后流无法再次使用。终端操作会产生一个新的集合或值。一般就是对流进行收集,也就是将多次加工之后的数据放入一直新的集合对象中,或者直接返回成其他的形式的集合或者对象,最后结束操作,我们就获得我们想要的集合对象。
    另外,Stream有几个特性:
  3. stream不存储数据,而是按照特定的规则对数据进行计算,一般会输出结果。说简单点,一个stream流是一个集合的加工机器,仅仅用于对数据进行加工处理。
  4. stream不会改变数据源,通常情况下会产生一个新的集合或一个值。就像刚才说的,最后处理完之后,原来的集合中的数据是不会变的,只是生成一个新的集合返回。
  5. stream具有延迟执行特性,只有调用终端操作时,中间操作才会执行。只有你调用手收集的相关操作,所有的流才会操作。类似于懒加载。

2.Stream的创建

创建stream流的方式有三种:

  • 第一种是集合数组的方式创建。
  • 第二种是Collection/Map的方式创建。
  • 第三种是使用Stream的静态方法。

2.1 集合的方式创建流

Stream可以通过集合或者数组创建。
1、通过 java.util.Collection.stream() 方法用集合创建流

List<String> list = Arrays.asList("a", "b", "c");
// 创建一个顺序流
Stream<String> stream = list.stream();
// 创建一个并行流
Stream<String> parallelStream = list.parallelStream();

2.2 使用数组的方式创建

使用java.util.Arrays.stream(T[] array)方法用数组创建流

int[] array={1,3,5,6,8};
IntStream stream = Arrays.stream(array);

2.3 使用Stream静态方法的方式创建

使用Stream的静态方法:of()、iterate()、generate()

//这里请自行的去了解lambda表达式。
Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5, 6);
Stream<Integer> stream2 = Stream.iterate(0, (x) -> x + 3).limit(4);
stream2.forEach(System.out::println);

Stream<Double> stream3 = Stream.generate(Math::random).limit(3);
stream3.forEach(System.out::println);

2.4 顺序流和并行流(stream、parallelStream)

stream和parallelStream的简单区分: stream是顺序流,由主线程按顺序对流执行操作,而parallelStream是并行流,内部以多线程并行执行的方式对流进行操作,但前提是流中的数据处理没有顺序要求。例如筛选集合中的奇数,两者的处理不同之处如下图:
在这里插入图片描述
如果流中的数据量足够大,并行流可以加快处速度。
除了直接创建并行流,还可以通过parallel()把顺序流转换成并行流:

Optional<Integer> findFirst = list.stream().parallel().filter(x->x>6).findFirst();

3.Stream的使用

下面直接用案例的方式讲解stream流中的api:
先给一个员工的案例:

List<Person> personList = new ArrayList<Person>();
personList.add(new Person("Tom", 8900, "male", "New York"));
personList.add(new Person("Jack", 7000, "male", "Washington"));
personList.add(new Person("Lily", 7800, "female", "Washington"));
personList.add(new Person("Anni", 8200, "female", "New York"));
personList.add(new Person("Owen", 9500, "male", "New York"));
personList.add(new Person("Alisa", 7900, "female", "New York"));
class Person {
	private String name;  // 姓名
	private int salary; // 薪资
	private int age; // 年龄
	private String sex; //性别
	private String area;  // 地区
	// 构造方法
	public Person(String name, int salary, int age,String sex,String area) {
		this.name = name;
		this.salary = salary;
		this.age = age;
		this.sex = sex;
		this.area = area;
	}
	// 省略了get和set,请自行添加
}

3.1 Stream (foreach、find、match)

这里为了新手能够更好的理解Stream的使用,这里不做lambda表达式的简化。

  List<Integer> list = Arrays.asList(7, 6, 9, 3, 8, 2, 1);
        // 遍历输出符合条件的元素
        list.stream().forEach(new Consumer<Integer>() {
            @Override
            public void accept(Integer inputRes) {
                System.out.print(inputRes); //这里会打印出 7693821
            }
        });
        //没学过Optional的可以看我的文章:
        // https://blog.youkuaiyun.com/weixin_46269045/article/details/142681907
        
        // 匹配第一个(这里是有序的,所以不能用并行流。但是他不会返回一个数据对象,而是返回一个Optional,
        Optional<Integer> findFirst = list.stream().findFirst();
        Integer findFirstInt = findFirst.get();//结果是7
        // 匹配任意,这里返回(适用于并行流)
        Optional<Integer> findAny = list.parallelStream().findAny();
        Integer findAnyInt = findAny.get(); // 结果是任何一个数字
        // 是否包含符合特定条件的元素
        //判断这个集合是否包含一个及其以上的满足该条的结果
        //下面的结果返回true,因为7 9 8 都大于6
        boolean anyMatch = list.stream().anyMatch(new Predicate<Integer>() {
            @Override
            public boolean test(Integer inputRes) {
                return inputRes > 6;
            }
        });

3.2 筛选(filter)

筛选,是按照一定的规则校验流中的元素,将符合条件的元素提取到新的流中的操作。
案例一:筛选出Integer集合中大于7的元素,并打印出来:

list.stream().filter(new Predicate<Integer>() { //过滤
   @Override
  public boolean test(Integer inputRes) {
        return inputRes > 7; //凡是满足这个的结果,都会被收纳
    }
}).forEach(new Consumer<Integer>() { //遍历操作
   @Override
   public void accept(Integer inputRes) {
     System.out.println(inputRes); //输出8和9
}
  });
  //案例2----------------------------------------------------------------------------------------------
// 筛选员工中工资高于8000的人,并形成新的集合。 形成新集合依赖collect(收集),后文有详细介绍。
List<Person> personList = new ArrayList<>();
personList.add(new Person("Tom", 8900, 23, "male", "New York"));
personList.add(new Person("Jack", 7000, 25, "male", "Washington"));
personList.stream().filter(new Predicate<Person>() {
    @Override
    public boolean test(Person person) {
          return person.getSalary() > 8000; //筛选薪资是否大于8000
    }
}).forEach(new Consumer<Person>() {
  @Override
  public void accept(Person person) {
  System.out.println(person.getName()); //打印  Tom
     }
});

3.3 聚合(max/min/count)

max、min、count这些字眼你一定不陌生,没错,在mysql中我们常用它们进行数据统计。Java stream中也引入了这些概念和用法,极大地方便了我们对集合、数组的数据统计工作。它的作用就是统计集合的值。整体理解起来还是比较简单,这里上直接上案例:

1.max(求最大值):

//获取最长的字符串
        List<String> list = Arrays.asList("wang", "dany", "danyyy", "danywang", "danywangwang");
        Optional<String> optionalMax = list.stream().max(new Comparator<String>() {
            @Override
            public int compare(String o1, String o2) {
                return o1.length() - o2.length();
            }
        });
       String result =  optionalMax.get(); //danywangwang

2.min(和max正好相反,这里不做过多介绍)

3.count (统计个数)
一般count是统计个数的,我们可以知道符合筛选条件的count的个数为多少

	List<Integer> list = Arrays.asList(7, 6, 4, 8, 2, 11, 9);
		long count = list.stream().filter(x -> x > 6).count(); //这里是统计有多少个大于6的数字
		System.out.println("list中大于6的元素个数:" + count); //结果是4

3.4 map映射(map/flatMap)

映射,可以将一个流的元素按照一定的映射规则映射到另一个流中。比如我们可以将Person流的对象映射成为Person的name的对象,从而获取到Person姓名的集合,其中:

  1. map:接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素。
  2. flatMap:接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流。

这里不理解这两个流的实际意义的区别没关系,其实我也不太理解,咱们后面举几个例子可能就能够理解了,如果实在理解不了,以后遇到需要映射的操作的时候,用map即可。

map :

String[] lowerCase = { "aaa", "bbb", "ccc", "ddd" };
List<String> uppeerCaseList = Arrays.stream(lowerCase).map(new Function<String, String>() {
   @Override
   public String apply(String s) { //这里s就是原始集合的一个个的值
          return s.toUpperCase();//大写操作 这里返回的就是新的大写的值
       }
    }).collect(Collectors.toList());
/**
* 最终的效果就像下面的映射一样:
* aaa->AAA
* bbb->BBB
* ccc->CCC
* ddd->DDD
**/
 //将其最终大写化(最后uppeerCaseList 的数据是 AAA BBB CCC DDD )

flatMap:

//案例:两个字符数组合并成一个新的字符数组。
	List<String> list = Arrays.asList("m,k,l,a", "1,3,5,7");
		List<String> listNew = list.stream().flatMap(s -> {
			// 将每个元素转换成一个stream
			String[] split = s.split(",");
			Stream<String> s2 = Arrays.stream(split);
			return s2;
		}).collect(Collectors.toList());

		System.out.println("处理前的集合:" + list);
		System.out.println("处理后的集合:" + listNew);
//处理前的集合:[m-k-l-a, 1-3-5]
//处理后的集合:[m, k, l, a, 1, 3, 5]

看到这里就有疑问了,这个flatMap好像也并没有什么特殊的用处,有点多此一举?其实不然。有的集合想要映射成一个个数组或者集合相关的对象(就像上面案例一样),然后将一个个集合进一步处理,为了对一个个集合也进行流的操作,这里就可以直接用到flatMap了。
mapToInt、mapToLong、mapToDouble
显而易见,这三个就是将对象映射为int long double等对象的,这里就不过多举例子:看下下面的两个例子即可:

// 输出字符串集合中每个字符串的长度
    List<String> stringList = Arrays.asList("mu", "优快云", "hello",
            "world", "quickly");
    stringList.stream().mapToInt(String::length).forEach(System.out::println);
//---------------------------------------------------------------------------
    // 将int集合的每个元素增加1000
    List<Integer> integerList = Arrays.asList(4, 5, 2, 1, 6, 3);
    integerList.stream().mapToInt(x -> x + 1000).forEach(System.out::println);

除此之外,mapToXXX 之类的方法还有很对聚集的功能,比如求最大最小值、求和、求平均值等,这里就不需要我们去实现一些函数式接口来定义了:

   List<Double> doubleList = Arrays.asList(1.0, 2.0, 3.0, 4.0, 2.0);
        double average = doubleList.stream().mapToDouble(new ToDoubleFunction<Double>() {
            @Override
            public double applyAsDouble(Double value) {
                //这里可以做一些额外的处理操作,具体就不详细说明了。
                return value;
            }
        })
                .average()//直接求平均值,简单方便 还有其他的.sum()以及.max()等来求出总和以及最大值
                .getAsDouble();//获得到最后的值

3.5 归约

什么是规约,规约也叫缩减,就是将一个集合的值进行一步步的“合并”,比如,将集合:[1,2,3,4]一步步相加,就是1+2 = 3 ------> 3(这个是1+2得到的3)+ 3 = 7
----> 7 + 4 = 11 。类似这种累计操作的功能,就是归约。也就是【1】和【2】进行操作,得到的结果在以同样的规则和【3】进行操作,以此类推。
stream给我们提供了规约的api,这里给一个简单的例子:

 List<Integer> list = Arrays.asList(1, 3, 2, 8, 11, 4);
        // 求和方式1
        Optional<Integer> sum = list.stream().reduce(new BinaryOperator<Integer>() {
            @Override
            public Integer apply(Integer int1, Integer int2) {
                //对两个元素做操作,得到的结果作为第三个元素的int1
                return int1 + int2;
            }
        });//这里返回的不是一个规约后的结果,是一个Optional Optional不会的可以去看下我写的Optional讲解,最后得到的结果是这个集合的和
//-----------------------------------------------------------------------------------------------------
//咱们还可以通过它求一个集合中集合的最大值
 List<Integer> list = Arrays.asList(1, 3, 2, 8, 11, 4);
        // 求和方式1
        Optional<Integer> sum = list.stream().reduce(new BinaryOperator<Integer>() {
            @Override
            public Integer apply(Integer int1, Integer int2) {
                //最后求出集合的最大值
                return Math.max(int1,int2);
            }
        });//这里返回的不是一个规约后的结果,是一个Optional Optional不会的可以去看下我写的Optional讲解
    }

3.6 Stream收集

我们对集合的一个个元素进行处理之后,就需要将处理好的元素收集起来,这里,收集成什么样的数据结构,List,Map,还是Set等,就取决于你指定哪个最终的集合。
因为流不存储数据,那么在流中的数据完成处理后,需要将流中的数据重新归集到新的集合里。toList、toSet和toMap比较常用,另外还有toCollection、toConcurrentMap等复杂一些的用法
collect的收集可以说是stream中内容最繁多、功能最丰富的部分了。
其中:

收集的collect方法主要依赖java.util.stream.Collectors类内置的静态方法。

这点比较重要!

3.6.1 (toList、toSet、toMap)

这三个是我们平时开发中最常用的三个收集器,toList就是将处理好的数据流收集成为一个List集合。其他与此相似。toSet可以给最终的数据流做去重处理(注意这里去重对象的话记得要重写equals、haCode)。toMap的入参给我们提供了两个Function接口,入参是每个实体对象,然后你可以选择这个对象的哪个属性(也可以直接把对象作为key,一般不常用)作为key,什么作为value。这样就能将一个单列集合转成一个Map。下面给几个具体的简单案例来说明一下:

List<Integer> list = Arrays.asList(1, 6, 3, 4,4,4,4 6, 7, 9, 6, 20);
//先对集合做过滤,取2的倍数,然后最后收集为List集合(注意这里的listNew集合是个新的集合,不会影响list里面的元素)
List<Integer> listNew = list.stream().filter(x -> x % 2 == 0).collect(Collectors.toList());
//与上同理,但是4会被去重
Set<Integer> set = list.stream().filter(x -> x % 2 == 0).collect(Collectors.toSet());

//Map收集
List<Person> personList = new ArrayList<Person>();
personList.add(new Person("Tom", 8900, 23, "male", "New York"));
personList.add(new Person("Jack", 7000, 25, "male", "Washington"));
personList.add(new Person("Lily", 7800, 21, "female", "Washington"));
personList.add(new Person("Anni", 8200, 24, "female", "New York"));
		
List<Person> list = new ArrayList<>();
Map<?, Person> map = list.stream()
                .collect(Collectors.toMap(new Function<Person, String>() {
                //第一个函数式接口是返回key的
                    @Override
                    public String apply(Person person) {
                        return person.getName();
                    }
                }, new Function<Person, Person>() {
                //第二个是返回value的
                    @Override
                    public Person apply(Person person) {
                    //这里可以对每个元素进行二次处理,然后,返回作为value
                        return person;
                    }
                })); //最终我们通过toMap 将person集合变成以用户名为Key,person为value的map集合

		System.out.println("toList:" + listNew);
		System.out.println("toSet:" + set);
		System.out.println("toMap:" + map);

3.6.2 统计(count/averaging/max/min)

平时我们对集合进行聚合统计的时候(求最大值,最小值,平均值等),可以通过for循环等进行统计。有没有更加简洁优雅的办法呢?当然,Collectors提供了一系列用于数据统计的静态方法:

1.计数:count
2.平均值:averagingInt、averagingLong、averagingDouble
3.最值:maxBy、minBy
4.求和:summingInt、summingLong、summingDouble
5.统计以上所有:summarizingInt、summarizingLong、summarizingDouble


        List<Person> personList = new ArrayList<Person>();
        personList.add(new Person("Tom", 8900, 23, "male", "New York"));
        personList.add(new Person("Jack", 7000, 25, "male", "Washington"));
        personList.add(new Person("Lily", 7800, 21, "female", "Washington"));

        // 求总数
        Long count = personList.stream().collect(Collectors.counting());//求元素的总和

        //--------------------------------------------------------------------------------------------------------

        Double averageSalary = personList.stream().collect(Collectors.averagingDouble(new ToDoubleFunction<Person>() {
            @Override
            public double applyAsDouble(Person value) {
                return value.getSalary();//将需要统计的属性进行二次处理,转换为double,再进行统计
            }
        }));//求薪资的平均值(averagingLong,averagingInteger同理,只是最后的值的类型不同而已)
        
      //--------------------------------------------------------------------------------------------------------

        //求工资最多的那个person
        Person maxPerson = personList.stream().collect(Collectors.maxBy(new Comparator<Person>() {
            @Override
            public int compare(Person p1, Person p2) {
                return p1.getSalary() - p2.getSalary();//正数表示p1大,负数表示p2大,为零则相等
            }
        })).get();//求薪资的最大值的那个person(Collectors.maxBy的泛型为Optional,这里用get获取它的真实的值)
        
     //--------------------------------------------------------------------------------------------------------
        // 求工资之和
        Integer sumSalary = personList.stream().collect(Collectors.summingInt(new ToIntFunction<Person>() {
            @Override
            public int applyAsInt(Person person) {
                return person.getSalary();
            }
        }));//(summingXXX 其他类型同理)

     //--------------------------------------------------------------------------------------------------------

        //一次性统计所有信息
        DoubleSummaryStatistics statistics = personList.stream().collect(Collectors.summarizingDouble(new ToDoubleFunction<Person>() {
            @Override
            public double applyAsDouble(Person person) {
                return person.getSalary();//提供需要统计的那个属性字段
            }
        }));//结果会返回一个DoubleSummaryStatistics对象,里面会有平均值,最大最小等值的信息。
        System.out.println(statistics.getAverage());//平均值
        System.out.println(statistics.getMax());//最大值
        System.out.println(statistics.getCount());//最小值
     // --------------------------------------------------------------------------------------------------------   

3.6.3 分组(partitioningBy/groupingBy)

分区:将stream按条件分为两个Map,比如员工按薪资是否高于8000分为两部分。
分组:将集合分为多个Map,比如员工按性别分组。有单级分组和多级分组。

    List<Person> personList = new ArrayList<Person>();
        personList.add(new Person("Tom", 8900, 45, "male", "New York"));
        personList.add(new Person("Jack", 7000, 86, "male", "Washington"));
        personList.add(new Person("Lily", 7800, 23, "female", "Washington"));
        personList.add(new Person("Anni", 8200, 18, "female", "New York"));
        personList.add(new Person("Owen", 9500, 69, "male", "New York"));
        personList.add(new Person("Alisa", 7900, 75, "female", "New York"));

        // 将员工按薪资是否高于8000分组
        Map<Boolean, List<Person>> part = personList.stream().collect(Collectors.partitioningBy(new Predicate<Person>() {
            @Override
            public boolean test(Person person) {
                return person.getSalary() > 8000;
            }
        }));

        // 将员工按性别分组
        Map<String, List<Person>> group = personList.stream().collect(Collectors.groupingBy(new Function<Person, String>() {
            @Override
            public String apply(Person person) {
                return person.getSex();
            }
        }));

        // 将员工先按性别分组,再按地区分组
        Map<String, Map<String, List<Person>>> group2 = personList.stream().collect(Collectors.groupingBy(new Function<Person, String>() {
            @Override
            public String apply(Person person) {
                return person.getSex();
            }

        }, Collectors.groupingBy(new Function<Person, String>() {
            @Override
            public String apply(Person person) {
                return person.getCity();
            }
        })));

        System.out.println("员工按薪资是否大于8000分组情况:" + part);
        System.out.println("员工按性别分组情况:" + group);
        System.out.println("员工按性别、地区:" + group2);

结果:

员工按薪资是否大于8000分组情况:{false=[Test.Person(name=Jack, salary=7000, age=86, sex=male, city=Washington), Test.Person(name=Lily, salary=7800, age=23, sex=female, city=Washington), Test.Person(name=Alisa, salary=7900, age=75, sex=female, city=New York)], true=[Test.Person(name=Tom, salary=8900, age=45, sex=male, city=New York), Test.Person(name=Anni, salary=8200, age=18, sex=female, city=New York), Test.Person(name=Owen, salary=9500, age=69, sex=male, city=New York)]}
员工按性别分组情况:{female=[Test.Person(name=Lily, salary=7800, age=23, sex=female, city=Washington), Test.Person(name=Anni, salary=8200, age=18, sex=female, city=New York), Test.Person(name=Alisa, salary=7900, age=75, sex=female, city=New York)], male=[Test.Person(name=Tom, salary=8900, age=45, sex=male, city=New York), Test.Person(name=Jack, salary=7000, age=86, sex=male, city=Washington), Test.Person(name=Owen, salary=9500, age=69, sex=male, city=New York)]}
员工按性别、地区:{female={New York=[Test.Person(name=Anni, salary=8200, age=18, sex=female, city=New York), Test.Person(name=Alisa, salary=7900, age=75, sex=female, city=New York)], Washington=[Test.Person(name=Lily, salary=7800, age=23, sex=female, city=Washington)]}, male={New York=[Test.Person(name=Tom, salary=8900, age=45, sex=male, city=New York), Test.Person(name=Owen, salary=9500, age=69, sex=male, city=New York)], Washington=[Test.Person(name=Jack, salary=7000, age=86, sex=male, city=Washington)]}}

3.6.4 接合(joining)

joining可以将stream中的元素用特定的连接符(没有的话,则直接连接)连接成一个字符串。

        List<Person> personList = new ArrayList<Person>();
		personList.add(new Person("Tom", 8900, 23, "male", "New York"));
		personList.add(new Person("Jack", 7000, 25, "male", "Washington"));
		personList.add(new Person("Lily", 7800, 21, "female", "Washington"));

		String names = personList.stream().map(p -> p.getName()).collect(Collectors.joining(","));
		System.out.println("所有员工的姓名:" + names);//Tom,Jack,Lily  字符串
		List<String> list = Arrays.asList("A", "B", "C");
		String string = list.stream().collect(Collectors.joining("-"));
		System.out.println("拼接后的字符串:" + string);//A-B-C

3.6.5 归约(reducing)

Collectors类提供的reducing方法,相比于stream本身的reduce方法,增加了对自定义归约的支持。

        final List<Person> personList = new ArrayList<Person>();
        personList.add(new Person("Tom", 8900, 45, "male", "New York"));
        personList.add(new Person("Jack", 7000, 86, "male", "Washington"));
        personList.add(new Person("Lily", 7800, 23, "female", "Washington"));
        personList.add(new Person("Anni", 8200, 18, "female", "New York"));
        personList.add(new Person("Owen", 9500, 69, "male", "New York"));
        personList.add(new Person("Alisa", 7900, 75, "female", "New York"));


        Integer sum = personList.stream().collect(Collectors.reducing(0, new Function<Person, Integer>() {
            @Override
            public Integer apply(Person person) {
                return person.getSalary();//对象的哪一个属性作为规约的目标
            }
        }, new BinaryOperator<Integer>() {
            @Override
            public Integer apply(Integer salary1, Integer salary2) { //规约的具体动作
                //  其中salary1 表示上一次规约的结果,salary2表示下一个被规约的元素的值
                return salary1 + salary2;
            }
        }));
        System.out.println("sum = " + sum);//sum=49300

3.7 排序(sorted)

sorted,中间操作。有两种排序:
sorted():自然排序,流中元素需实现Comparable接口
sorted(Comparator com):Comparator排序器自定义排序

 List<Person> personList = new ArrayList<Person>();

        personList.add(new Person("Sherry", 9000, 24, "female", "New York"));
        personList.add(new Person("Tom", 8900, 22, "male", "Washington"));
        personList.add(new Person("Jack", 9000, 25, "male", "Washington"));
        personList.add(new Person("Lily", 8800, 26, "male", "New York"));
        personList.add(new Person("Alisa", 9000, 26, "female", "New York"));

        // 按工资升序排序(自然排序)
        List<String> newList = personList.stream().sorted(Comparator.comparing(new Function<Person, Integer>() {
                    @Override
                    public Integer apply(Person person) {//根据person的哪个字段排序
                        return person.getSalary();
                    }
                }))
                .map(Person::getName)
                .collect(Collectors.toList());
        
        // 按工资倒序排序(reversed())
        List<Person> newList1 = personList.stream().sorted(Comparator.comparing(new Function<Person, Integer>() {
                    @Override
                    public Integer apply(Person person) {
                        return person.getSalary();
                    }
                }).reversed())//这里添加一个reversed,可以逆序排序
                .collect(Collectors.toList());
        
        // 先按工资再按年龄升序排序
        List<Person> newList3 = personList.stream()
                .sorted(Comparator.comparing(Person::getSalary)
                        .thenComparing(Person::getAge))
                .collect(Collectors.toList());

        // 先按工资再按年龄自定义排序(降序)
        List<Person> newList4 = personList.stream().sorted((p1, p2) -> {
            if (Objects.equals(p1.getSalary(), p2.getSalary())) {
                return p2.getAge() - p1.getAge();
            } else {
                return p2.getSalary() - p1.getSalary();
            }
        }).collect(Collectors.toList());

3.8 提取/组合

流也可以进行合并、去重、限制、跳过等操作。

public class StreamTest {
	public static void main(String[] args) {
		String[] arr1 = { "a", "b", "c", "d" };
		String[] arr2 = { "d", "e", "f", "g" };

		Stream<String> stream1 = Stream.of(arr1);
		Stream<String> stream2 = Stream.of(arr2);
		// concat:合并两个流 distinct:去重
		List<String> newList = Stream.concat(stream1, stream2).distinct().collect(Collectors.toList());
		// limit:限制从流中获得前n个数据
		List<Integer> collect = Stream.iterate(1, x -> x + 2).limit(10).collect(Collectors.toList());
		// skip:跳过前n个数据
		List<Integer> collect2 = Stream.iterate(1, x -> x + 2).skip(1).limit(5).collect(Collectors.toList());

		System.out.println("流合并:" + newList);
		System.out.println("limit:" + collect);
		System.out.println("skip:" + collect2);
	}
}
// 流合并:[a, b, c, d, e, f, g]
// limit:[1, 3, 5, 7, 9, 11, 13, 15, 17, 19]
// skip:[3, 5, 7, 9, 11]

总结

以上就是stream的所有的常规的用法了。日常开发中,我们尽量用stream处理集合,不仅方便快捷而且代码简单。以上我没有用lambda表达式,是为了方便初学者理解调用的哪个函数是接口。在实际开发中,尽量去用lambd表达式和方法引用去简化代码的书写。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值