lamdba03 Java8新特性之四:Stream API

Java8新特性之四:Stream API


Java8的两个重大改变,一个是Lambda表达式,另一个就是Stream API表达式。 Stream 是Java8中处理集合的关键抽象概念,它可以对集合进行非常复杂的查找、过滤、筛选等操作,在新版的JPA【连接数据库】中,也已经加入了Stream。
Stream API给我们操作集合带来了强大的功用,同时Stream API操作简单,容易上手。

例如

@Query("select u from User u")
Stream<User> findAllByCustomQueryAndStream();

Stream<User> readAllByFirstnameNotNull();

@Query("select u from User u")
Stream<User> streamAllPaged(Pageable pageable);

Stream的操作步骤

Stream有如下三个操作步骤:

一、创建Stream
从一个数据源,如集合、数组中获取流。
二、中间操作
一个操作的中间链,对数据源的数据进行操作。
三、终止操作
一个终止操作,执行中间操作链,并产生结果。
注意:对流的操作完成后需要进行关

举个简单的例子:

假设有一个Person类和一个Person列表,现在有两个需求:1)找到年龄大于18岁的人并输出;2)找出所有中国人的数量。

@Data
class Person {
    private String name;
    private Integer age;
    private String country;
    private char sex;

    public Person(String name, Integer age, String country, char sex) {
        this.name = name;
        this.age = age;
        this.country = country;
        this.sex = sex;
    }
}
List<Person> personList = new ArrayList<>();
personList.add(new Person("欧阳雪",18,"中国",'F'));
personList.add(new Person("Tom",24,"美国",'M'));
personList.add(new Person("Harley",22,"英国",'F'));
personList.add(new Person("向天笑",20,"中国",'M'));
personList.add(new Person("李康",22,"中国",'M'));
personList.add(new Person("小梅",20,"中国",'F'));
personList.add(new Person("何雪",21,"中国",'F'));
personList.add(new Person("李康",22,"中国",'M'));

在JDK8以前,我们可以通过遍历列表来完成。但是在有了Stream API后,可以这样来实现:

 public static void main(String[] args) {
        List<Person> personList = new ArrayList<>();
        personList.add(new Person("欧阳雪", 18, "中国", 'F'));
        personList.add(new Person("Tom", 24, "美国", 'M'));
        personList.add(new Person("Harley", 22, "英国", 'F'));
        personList.add(new Person("向天笑", 20, "中国", 'M'));
        personList.add(new Person("李康", 22, "中国", 'M'));
        personList.add(new Person("小梅", 20, "中国", 'F'));
        personList.add(new Person("何雪", 21, "中国", 'F'));
        personList.add(new Person("李康", 22, "中国", 'M'));


//    1)找到年龄大于18岁的人并输出;
//创建stream对象      boolean test(T t);      void accept(T t);
        personList.stream().filter(t->t.getAge()>20).forEach(t->System.out.println(t));
//        lamdba 里面的方法引用
personList.stream().filter(t->t.getAge()>20).forEach(System.out::println);

//    2)找出所有中国人的数量。  count()计算数量
    long c = personList.stream().filter(t -> t.getCountry().equals("中国")).count();

        System.out.println(c);
    }

在这个例子中,personList.stream()是创建流,filter()属于中间操作,forEach、count()是终止操作。

Stream中间操作–筛选与切片 与parallelStream并行流

filter:接收Landba,从流中排除某些操作
limit:截断流,使其元素不超过给定对象
skip(n):跳过元素,返回一个扔掉了前n个元素的流,若流中不足n个,则返回一个空流,与limit(n)互补
distinct:筛选,通过流所生成元素的hashCode()和equals()去除重复元素。

limit举例

需求,从Person列表中取出两个女性。

//        从Person列表中取出两个女性。 limit
      personList.stream().filter(t -> t.getSex() == 'F').limit(2).forEach(System.out::println);

skip举例

从Person列表中从第2个女性开始,取出所有的女性。

//        从Person列表中从第2个女性开始,取出所有的女性。  skip(1)表示跳过第一个
        personList.stream().filter(t->t.getSex()=='F').skip(1).forEach(System.out::println);

distinct举例

//        去重
        personList.stream().distinct().forEach(System.out::println);

Stream中间操作–映射

  • map–接收Lambda,将元素转换成其他形式或提取信息。接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素。
  • flatMap–接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流

map举例

比如,我们用一个PersonCountry类来接收所有的国家信息

//        比如,我们用一个PersonCountry类来接收所有的国家信息       R apply(T t);
        personList.stream().map(t->t.getCountry()).forEach(System.out::println);

接收所有的姓名和国家 首先封装一个两个字段的实体类

//      接收所有的姓名和国家  首先封装一个两个字段的实体类
        personList.stream().map(t->new NameAndCountry(t.getName(),t.getCountry())).forEach(System.out::println);
@Data
@AllArgsConstructor
@NoArgsConstructor
class  NameAndCountry{
    private String name;
    private String country;
}

对前面的personList按照年龄从小到大排序,年龄相同,再按照姓名排序

//对前面的personList按照年龄从小到大排序,年龄相同,再按照姓名排序
//使用无参必须该类实现自然排序
//        personList.stream().sorted();
//           int compare(T o1, T o2);
//        第一种方法
        personList.stream().sorted((o1, o2) -> {
            if (o1.getAge() == o2.getAge()) {
                return o1.getName().compareTo(o2.getName());
            }
            return o1.getAge() - o2.getAge();
        }).forEach(System.out::println);
        System.out.println("-----------------");
//第二种方法 使用三目运算符
        personList.stream().sorted((o1, o2) ->o1.getAge()==o2.getAge()?o1.getName().compareTo(o2.getName()):o1.getAge()-o2.getAge()).forEach(System.out::println);

总结

1. filter(Predicate)
2. limit(long)
3. skip(long)
4. distinct();
5. map(Function)
6. sorted()  | sorted(Comparator)

终止操作–查找与匹配

  • allMatch–检查是否匹配所有元素
  • anyMatch–检查是否至少匹配一个元素
  • noneMatch–检查是否没有匹配所有元素
  • findFirst–返回第一个元素
  • findAny–返回当前流中的任意元素
  • count–返回流中元素的总个数
  • max–返回流中最大值
  • min–返回流中最小值

这些方面在Stream类中都有说明,这里不一一举例,只对allMatch、max各举一例进行说明。

allMatch检查是否至少匹配一个元素

判断personList中的人是否都是大于18岁的

//检查是否至少匹配一个元素
        boolean b = personList.stream().anyMatch(item -> item.getAge() >18);
        System.out.println(b);

判断personList中的人是否都是成年人

//        判断personList中的人是否都是中国人:
        boolean b1 = personList.stream().allMatch(p -> p.getCountry().equals("中国"));
        System.out.println(b1);

findAny返回当前流中的任意元素

lamdba03 Java8新特性之四:Stream API

//返回当前流中的任意元素
        Optional<Person> any = personList.stream().findAny();
        System.out.println(any);
//返回当前流中的任意元素
//        Person person = personList.stream().findAny().get();
        Person person = personList.parallelStream().findAny().get();
        System.out.println(person);

max-返回流中最大值

personList中找到年龄最大的

//        找到年龄最大的      int compare(T o1, T o2);
        Optional<Person> max = personList.stream().max((o1, o2) -> o1.getAge() - o2.getAge());
        System.out.println(max);

归约

Stream API的归约操作可以将流中元素反复结合起来,得到一个值,有

Optional<T> reduce(BinaryOperator<T> accumulator);

T reduce(T identity, BinaryOperator<T> accumulator);

<U> U reduce(U identity,
                 BiFunction<U, ? super T, U> accumulator,
                 BinaryOperator<U> combiner);

求一个1到100的和

第一种方法:
用到了reduce第二个方法:T reduce(T identity, BinaryOperator accumulator)

        //        求1-100的和  规约
        List<Integer> list = new ArrayList<>();
        for (int i = 0; i < 10; i++) {
            list.add(new Random().nextInt(10));
        }
        System.out.println(list);
//    R apply(T t, T u,T r);
        Optional<Integer> reduce = list.stream().reduce((t, u) -> t + u);
        System.out.println(reduce);

把这个动作拆解一下,其运算步骤模拟如下:
0 (1,2) -> 1 + 2 + 0
3 (3,4) -> 3 + 4 + 3
10 (5,6) -> 5 + 6 + 10
其运算步骤是,每次将列表的两个元素相加,并将结果与前一次的两个元素的相加结果进行累加,因此,在开始时,将identity设为0,因为第1个元素和第2个元素在相加的时候,前面还没有元素操作过
第二种方法:

     Integer[] list = {1, 2, 3, 4, 5};
        System.out.println(Arrays.asList(list));

        Arrays.stream(list).reduce((t,u)->{
            System.out.println(t+"-----"+u);
            return t+u;
        });
        System.out.println(Arrays.asList(list));

求所有人的年龄之和

//        求所有人的年龄之和
        Optional<Integer> reduce = personList.stream().map(item -> item.getAge()).reduce((t, u) -> t + u);
        System.out.println(reduce);

求所有人的平均年龄

Collectors.averagingDouble

//averagingDouble
        Double collect1 = personList.stream().collect(Collectors.averagingDouble(t -> t.getAge()));
        System.out.println(collect1);

求所有人的年龄的集合

collect(Collectors.toList())

        List<Integer> collect = personList.stream().map(item -> item.getAge()).collect(Collectors.toList());

lamdba03 Java8新特性之四:Stream API

求集合中所有人的最大年龄

//        求出集合中最大年龄
        Optional<Person> collect2 = personList.stream().collect(Collectors.maxBy((o1, o2) -> o1.getAge() - o2.getAge()));
        System.out.println(collect2);

最小年龄类型。
还有其他很操作,可以参考java.util.stream.Collectors。

注意流的关闭

求集合中所有人的最小年龄。

 try(final Stream<Integer> integerStream = personList.stream().map(Person::getAge)) {
   final Optional<Integer> minAge = integerStream.collect(Collectors.minBy(Integer::compareTo));
   System.out.println(minAge.get());
}

最好将流的操作放到try-with-resources,本章前面内容为了方便,没有放到try-with-resources中。

完整测试代码

package com.zz.lamdab.lamdba04;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.stream.Stream;

public class Deom1 {
    public static void main(String[] args) {
        List<Person> personList = new ArrayList<>();
        personList.add(new Person("欧阳雪", 18, "中国", 'F'));
        personList.add(new Person("Tom", 24, "美国", 'M'));
        personList.add(new Person("Harley", 22, "英国", 'F'));
        personList.add(new Person("向天笑", 20, "中国", 'M'));
        personList.add(new Person("李康", 22, "中国", 'M'));
        personList.add(new Person("小梅", 20, "中国", 'F'));
        personList.add(new Person("何雪", 21, "中国", 'F'));
        personList.add(new Person("李康", 22, "中国", 'M'));


//    1)找到年龄大于18岁的人并输出;
//创建stream对象      boolean test(T t);      void accept(T t);
        personList.stream().filter(t -> t.getAge() > 20).forEach(t -> System.out.println(t));
//        lamdba 里面的方法引用
        personList.stream().filter(t -> t.getAge() > 20).forEach(System.out::println);

//    2)找出所有中国人的数量。  count()计算数量
        long c = personList.stream().filter(t -> t.getCountry().equals("中国")).count();

        System.out.println(c);

//        从Person列表中取出两个女性。 limit
        personList.stream().filter(t -> t.getSex() == 'F').limit(2).forEach(System.out::println);

//        从Person列表中从第2个女性开始,取出所有的女性。  skip(1)表示跳过第一个
        personList.stream().filter(t -> t.getSex() == 'F').skip(1).forEach(System.out::println);
        System.out.println("-----------------");
//        去重
        personList.stream().distinct().forEach(System.out::println);
        System.out.println("-----------------");

//        比如,我们用一个PersonCountry类来接收所有的国家信息       R apply(T t);
        personList.stream().map(t -> t.getCountry()).forEach(System.out::println);
//      接收所有的姓名和国家  首先封装一个两个字段的实体类
        personList.stream().map(t -> new NameAndCountry(t.getName(), t.getCountry())).forEach(System.out::println);

        System.out.println("-----------------");
//对前面的personList按照年龄从小到大排序,年龄相同,再按照姓名排序
//使用无参必须该类实现自然排序
//        personList.stream().sorted();
//           int compare(T o1, T o2);
//        第一种方法
        personList.stream().sorted((o1, o2) -> {
            if (o1.getAge() == o2.getAge()) {
                return o1.getName().compareTo(o2.getName());
            }
            return o1.getAge() - o2.getAge();
        }).forEach(System.out::println);
        System.out.println("-----------------");
//第二种方法 使用三目运算符
        personList.stream().sorted((o1, o2) ->o1.getAge()==o2.getAge()?o1.getName().compareTo(o2.getName()):o1.getAge()-o2.getAge()).forEach(System.out::println);
        System.out.println("-----------------");
//返回当前流中的任意元素
        Optional<Person> any = personList.stream().findAny();
        System.out.println(any);
        System.out.println("-----------------");
//        检查是否至少匹配一个元素
        boolean b = personList.stream().anyMatch(item -> item.getAge() >18);
        System.out.println(b);
//        判断personList中的人是否都是中国人:
        boolean b1 = personList.stream().allMatch(p -> p.getCountry().equals("中国"));
        System.out.println(b1);
//返回当前流中的任意元素
//        Person person = personList.stream().findAny().get();
        Person person = personList.parallelStream().findAny().get();
        System.out.println(person);

//        找到年龄最大的      int compare(T o1, T o2);
        Optional<Person> max = personList.stream().max((o1, o2) -> o1.getAge() - o2.getAge());
        System.out.println(max);



//        求所有人的年龄之和
        Optional<Integer> reduce = personList.stream().map(item -> item.getAge()).reduce((t, u) -> t + u);
        System.out.println(reduce);

        List<Integer> collect = personList.stream().map(item -> item.getAge()).collect(Collectors.toList());
//averagingDouble
        Double collect1 = personList.stream().collect(Collectors.averagingDouble(t -> t.getAge()));
        System.out.println(collect);
        System.out.println(collect1);
//        求出集合中最大年龄
        Optional<Person> collect2 = personList.stream().collect(Collectors.maxBy((o1, o2) -> o1.getAge() - o2.getAge()));
        System.out.println(collect2);
//        注意流的关闭
//        Stream<Integer> integerStream = personList.stream().map(Person::getAge);


        try(final Stream<Integer> integerStream = personList.stream().map(Person::getAge)) {
            final Optional<Integer> minAge = integerStream.collect(Collectors.minBy(Integer::compareTo));
            System.out.println(minAge.get());

        }

    }


}

@Data
class Person {
    private String name;
    private Integer age;
    private String country;
    private char sex;

    public Person(String name, Integer age, String country, char sex) {
        this.name = name;
        this.age = age;
        this.country = country;
        this.sex = sex;
    }
}

@Data
@AllArgsConstructor
@NoArgsConstructor
class NameAndCountry {
    private String name;
    private String country;
}

上一篇:Stream流


下一篇:Java8新特性之Stream--Stream方法