Java8-Stream API 详解

2020-12-14 15:16:03 浏览数 (1)

参考链接: 如何在Java 8中打印Stream的元素

摘要 

 Stream 作为 Java 8 的一大亮点,它与 java.io 包里的 InputStream 和 OutputStream 是完全不同的概念。它也不同于 StAX 对 XML 解析的 Stream,也不是 Amazon Kinesis 对大数据实时处理的 Stream。Java 8 中的 Stream 是对集合(Collection)对象功能的增强,它专注于对集合对象进行各种非常便利、高效的聚合操作(aggregate operation),或者大批量数据操作 (bulk data operation)。Stream API 借助于同样新出现的 Lambda 表达式,极大的提高编程效率和程序可读性。同时它提供串行和并行两种模式进行汇聚操作,并发模式能够充分利用多核处理器的优势,使用 fork/join 并行方式来拆分任务和加速处理过程。通常编写并行代码很难而且容易出错, 但使用 Stream API 无需编写一行多线程的代码,就可以很方便地写出高性能的并发程序。所以说,Java 8 中首次出现的 java.util.stream 是一个函数式语言 多核时代综合影响的产物。 

简介 

流(Stream)是数据通道,用于操作数据源(集合,数组等)所生成的元素序列 “集合讲的的是数据,流讲的是计算” 注意:  ① Stream不会存储元素  ② Stream不会改变源对象,相反他们会返回一个持有结果的新的Stream  ③ Stream操作是延迟执行的,这意味着他们等到需要结果的时候才会执行(惰性求值) 

Stream操作的三个步骤 

创建Stream 一个数据源(如:集合,数组)获取一个流中间操作 一个中间操作链,对数据源的数据进行处理终止操作(终端操作) 一个终止操作,执行中间操作链,并产生结果  

一:创建Stream 

Collection提供了两个方法.stream()与paralleStream() 

@org.junit.Test

public void test4(){

    List<Integer> list = new ArrayList<>();

    Stream<Integer> stream = list.stream();//串行流

    Stream<Integer> integerStream = list.parallelStream();//并行流

}

通过Arrays中的Stream()获取一个数组流。 

 Integer[] integers ={};

 Stream<Integer> stream1 = Arrays.stream(integers);

通过Stream类中静态方法of() 

Stream<String> stream2 = Stream.of("aaa", "bbb");

创建无限流(无穷的数据) 

  生成 //通过生成器产生5个10以内的随机数,如果不使用limit就会无限生成10以内随机数

Stream.generate(() -> Math.random() * 10).limit(5).forEach(System.out::println);

----------输出--------

0.8320556195819129

6.260534125204207

7.344094646332503

0.18490598959698068

6.392272744710005

  迭代 //通过迭代的方式(一元运算)生成5个数

Stream.iterate(0,x->x 2).limit(5).forEach(System.out::println);

-------------------输出------------

0

2

4

6

8

二:中间操作 

 多个中间操作可以连接起来形成一个流水线,除非流水线上触发终止操作,否则中间操作不会执行任何的处理,而在终止操作时一次性全部执行,称为“惰性求值” 

 List<Integer> list = Arrays.asList(1,2,3,523,21,55);

Stream<Integer> stream3 = list.stream().filter(x -> {

         System.out.println("函数执行");

         return x > 10;

 // stream3.forEach(System.out::println);

 上面的代码没有终止操作,当你运行时不会打印任何东西 

①筛选与切片 

filter----接收Lambda,从流中排除某些元素 

//filter()中需要使用断言型接口(Predicate)

List<Integer> list = Arrays.asList(1,2,3,523,21,55);

Stream<Integer> stream3 = list.stream().filter(x -> x > 10);

stream3.forEach(System.out::println);

limit----截断流,使其元素不超过给定数量 

List<Integer> list = Arrays.asList(1,2,3,523,21,55);

Stream<Integer> stream3 = list.stream().limit(3);

stream3.forEach(System.out::println);

--------------------输出---------------------

1

2

3

skip----跳过元素返回一个抛弃了前n个元素的流,若流中元素不满足n个,则返回一个空流,与limit形成互补 

List<Integer> list = Arrays.asList(1,2,3,523,21,55);

Stream<Integer> stream3 = list.stream().skip(3);

stream3.forEach(System.out::println);

--------------------输出---------------------

523

21

55

distinct----筛选,通过流所所生成元素的hashCode()和equals()去除重复元素 

  List<Integer> list = Arrays.asList(1,2,3,3,2,4);

  Stream<Integer> stream3 = list.stream().distinct();

  stream3.forEach(System.out::println);

  --------------------输出---------------------

  1

  2

  3

  4

注意:自定义的实体类使用distinct去重时,一定要先重写hashCode()和equals() 

②映射 

map----接收Lambda,将元素转换为其他形式或提取信息时,接收一个函数作为参数,该函数被应用到每个元素上,并将其映射成一个新的元素//map()里面使用函数型接口(Function)

List<String> list = Arrays.asList("aa","bb","cc");

Stream<String> stream3 = list.stream().map(String::toUpperCase);

stream3.forEach(System.out::println);

----------------------输出-----------------------

AA

BB

CC

------------------------------------------------

集合里的每一个元素都会使用到String.toUpperCase()方法

它是以aa作为一个元素,bb作为一个元素 

 flatMap----接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接一个流List<String> list = Arrays.asList("aa","bb","cc");

       Stream<String> stream3 = list.stream().flatMap(l -> {

           String[] strings = l.split("");

           return Arrays.stream(strings);

       });

stream3.forEach(System.out::println);

-------------------输出-----------

a

a

b

b

c

c

-----------------------------------------------

flatMap将原来的流转换为一个新的流并且,是以每一个值为单位的

③排序 

sorted() 自然排序 按照Comparable的方式List<String> list = Arrays.asList("aa","cc","bb");

Stream<String> stream3 = list.stream().sorted();

stream3.forEach(System.out::println);

---------------输出-----------

aa

bb

cc

 sorted( Comparator com)定制排序 List<Integer> list = Arrays.asList(1,3,2,6,8,3,9);

 Stream<Integer> stream3 = list.stream().sorted(Integer::compare);

 stream3.forEach(System.out::println);

 --------------输出--------------

 1

 2

 3

 3

 6

 8

 9

终止操作 

①查找与匹配 

allMatch----检查是否匹配所有元素//allMatch()里面的时断言型接口(Predicate)

 List<Integer> list = Arrays.asList(1,3,2,6,8,3,9);

 boolean b = list.stream().allMatch(x -> x > 3);

 System.out.println(b);

 ------------------输出--------------------

 false

 //因为不是所有的数都大于3

 anyMatch----检查是否有匹配至少一个元素//anyMatch()里面的时断言型接口(Predicate)

 List<Integer> list = Arrays.asList(1,3,2,6,8,3,9);

 boolean b = list.stream().anyMatch(x -> x > 3);

 System.out.println(b);

  ------------------输出--------------------

 true

 //只要有大于3的数就返回true

 noneMatch----检查是否没有匹配的元素//noneMatch()里面的时断言型接口(Predicate)

List<Integer> list = Arrays.asList(1,3,2,6,8,3,9);

boolean b = list.stream().noneMatch(x -> x > 3);

System.out.println(b);

 ------------------输出--------------------

 false

 //双重否定,返回false就是有匹配的元素

 findFirst----返回第一个元素List<Integer> list = Arrays.asList(1,3,2,6,8,3,9);

Optional<Integer> first = list.stream().findFirst();

System.out.println(first.get());

-----------------输出----------------

1

 findAny----返回当前流中的任意一元素List<Integer> list = Arrays.asList(1,3,2,6,8,3,9);

Optional<Integer> first = list.stream().findAny();

System.out.println(first.get());

-----------------输出----------------

1

 count-----返回流中元素的总数List<Integer> list = Arrays.asList(1,3,2,6,8,3,9);

long count = list.stream().count();

System.out.println(count);

-----------------输出----------------

7

 max----返回流中最大值 List<Integer> list = Arrays.asList(1,3,2,6,8,3,9);

 Optional<Integer> max = list.stream().max(Integer::compareTo);

 System.out.println(max.get());

 -----------------输出----------------

9

 min----返回流中的最小值List<Integer> list = Arrays.asList(1,3,2,6,8,3,9);

Optional<Integer> min = list.stream().min(Integer::compareTo);

System.out.println(min.get());

 -----------------输出----------------

1

 forEach----遍历流中的元素 List<Integer> list = Arrays.asList(1,3,2,6,8,3,9);

 list.stream().forEach(System.out::println);

 -----------------输出----------------

1

3

2

6

8

3

9

//注意:forEach的迭代操作是由Stream API完成的称为内部迭代

//借助于iterator的方式为外部迭代

②归约 

reduce(T identity,BinaryOperator)—可以将流中元素反复结合起来得到一个值,返回T List<Integer> list = Arrays.asList(1,3,2,6,8,3,9);

 Integer reduce = list.stream().reduce(0, (x, y) -> x y);

 System.out.println(reduce);

 -----------------输出----------------

 32

 //根据2元运算将所有的数加起来

 //首先以0为x,1为y,结果为1,然后1为x,取3为y,结果为4,以4为x...以此类推

 reduce(BinaryOpreator)----可以将流中元素反复结合起来,返回Optional< T > List<Integer> list = Arrays.asList(1,3,2,6,8,3,9);

 Optional<Integer> reduce = list.stream().reduce((x, y) -> x y);

 System.out.println(reduce.get());

  -----------------输出----------------

 32

 //原理同上,只是这里没有初始值,直接取1为x

 //所以ist就有可能为空,当返回的值可能为空时,结果存储在Optional容器中,避免空指针异常

③收集 

collect----将流转换为其他形式,接收一个Collector接口的实现,用于给Stream中元素做汇总的方法 

 Colloector 接口中方法的实现决定了如何对流执行手机操作(如收集到List、Set、Map中)但是Collectots实用类提供了很多静态方法,可以方便的创建常见收集器实例 

接下来进行详细介绍 首先创建一个实体类 

public class User {

    private String name;

    private Integer age;

    private double salary;

    public User(String name, Integer age, double salary) {

        this.name = name;

        this.age = age;

        this.salary = salary;

    }

    public String getName() {

        return name;

    }

    public void setName(String name) {

        this.name = name;

    }

    public Integer getAge() {

        return age;

    }

    public void setAge(Integer age) {

        this.age = age;

    }

    public double getSalary() {

        return salary;

    }

    public void setSalary(double salary) {

        this.salary = salary;

    }

    @Override

    public String toString() {

        return "User{"

                "name='" name '''

                ", age=" age

                ", salary=" salary

                '}';

    }

    @Override

    public boolean equals(Object o) {

        if (this == o) return true;

        if (o == null || getClass() != o.getClass()) return false;

        User user = (User) o;

        return Double.compare(user.salary, salary) == 0 &&

                Objects.equals(name, user.name) &&

                Objects.equals(age, user.age);

    }

    @Override

    public int hashCode() {

        return Objects.hash(name, age, salary);

    }

}

在测试类中准备好数据 

public class StreamTest {

     List<User> user = Arrays.asList(new User("张三",12,1000.00),

                                    new User ("李四",32,4000),

                                    new User ("王五",40,4000),

                                    new User ("王五",40,4000));

}

根据名称生成一个新的List 

 List<String> list = user.stream().map(User::getName).collect(Collectors.toList());

 list.forEach(System.out::println);

 -----------------输出--------------

 张三

 李四

 王五

 王五

根据名称生成一个新的Set 

 Set<String> set = user.stream().map(User::getName).collect(Collectors.toSet());

 set.forEach(System.out::println);

 -----------------输出--------------

 张三

 李四

 王五

根据名称生成一个新的HashSet 

 HashSet<String> hashSet = user.stream().map(User::getName).collect(Collectors.toCollection(HashSet::new));

 hashSet.forEach(System.out::println);

 -----------------输出--------------

 李四

 张三

 王五

获取流中的元素总数 

Long count = user.stream().collect(Collectors.counting());

System.out.println(count);

-----------------输出--------------

4

根据工资获取平均值 

Double avg = user.stream().collect(Collectors.averagingDouble(User::getSalary));

System.out.println(avg);

-----------------输出--------------

3250.0

根据工资获取总和 

Double sum = user.stream().collect(Collectors.summingDouble(User::getSalary));

System.out.println(sum);

-----------------输出--------------

13000.0

根据工资获取组函数 

 DoubleSummaryStatistics sum = user.stream().collect(Collectors.summarizingDouble(User::getSalary));

 System.out.println(sum);

 -----------------输出--------------

 DoubleSummaryStatistics{count=4, sum=13000.000000, min=1000.000000, average=3250.000000, max=4000.000000}

根据工资获取最大值 

 Optional<User> max = user.stream().collect(Collectors.maxBy(Comparator.comparingDouble(User::getSalary)));

 System.out.println(max.get());

 -----------------输出--------------

 User{name='李四', age=32, salary=4000.0}

根据工资获取最小值 

 Optional<User> min = user.stream().collect(Collectors.minBy(Comparator.comparingDouble(User::getSalary)));

 System.out.println(min.get());

 -----------------输出--------------

 User{name='张三', age=12, salary=1000.0}

分组 

Map<Double, List<User>> map = user.stream().collect(Collectors.groupingBy(User::getSalary));

System.out.println(map);

-----------------输出--------------

{4000.0=[User{name='李四', age=32, salary=4000.0}, User{name='王五', age=40, salary=4000.0}, User{name='王五', age=40, salary=4000.0}], 1000.0=[User{name='张三', age=12, salary=1000.0}]}

多级分组 

Map<Double, Map<String, List<User>>> collect = user.stream().collect(Collectors.groupingBy(User::getSalary, Collectors.groupingBy(

                u -> {

                    if ( u.getAge() <= 12) {

                        return "青年";

                    } else if ( u.getAge() <= 32) {

                        return "中年";

                    } else {

                        return "老年";

                    }

                }

        )));

System.out.println(collect);    

-----------------输出--------------    

{4000.0={老年=[User{name='王五', age=40, salary=4000.0}, User{name='王五', age=40, salary=4000.0}], 中年=[User{name='李四', age=32, salary=4000.0}]}, 1000.0={青年=[User{name='张三', age=12, salary=1000.0}]}}

分区 

Map<Boolean, List<User>> collect1 = user.stream().collect(Collectors.partitioningBy(e -> e.getSalary() > 3000));

System.out.println(collect1);

-----------------输出--------------   

{false=[User{name='张三', age=12, salary=1000.0}], true=[User{name='李四', age=32, salary=4000.0}, User{name='王五', age=40, salary=4000.0}, User{name='王五', age=40, salary=4000.0}]}

连接 

String s = user.stream().map(User::getName).collect(Collectors.joining("--"));

System.out.println(s);

-----------------输出--------------

张三--李四--王五--王五

0 人点赞