Java8新特性学习-Stream

524 阅读5分钟

Java8新特性学习-Stream😝

介绍

Java 8 新增了一个新的抽象称为流Stream,个人感觉这个超级好用,尤其是业务开发,经常需要对数据列表进行处理的时候,用更少的代码、更优雅地实现功能。

这个流式操作,更像是Linux中的管道命令'|',上一个操作的结果传送到下一个流程中继续处理,例如:

+--------------------+       +------+   +------+   +---+   +-------+
| stream of elements +-----> |filter+-> |sorted+-> |map+-> |collect|
+--------------------+       +------+   +------+   +---+   +-------+

在java代码实现

String content = "hello,world,debug,the,world";
        List<String> list = Arrays.asList(content.split(","));

                List<String> filter = list.stream()
                .filter(temp -> temp.startsWith("w"))
                .map(temp -> temp.substring(0, 3))
                .collect(Collectors.toList());

        filter.forEach(System.out::print);

什么是Stream

Stream(流)是一个来自数据源的元素队列并支持聚合操作

  • 元素是特定类型的对象,形成一个队列。Java中的Stream并不会存储元素,而是按需计算。
  • 数据源 流的来源可以是集合、数组、I/O Channel等
  • 聚合操作 类似SQL语句,比如filter、map、reduce、find、match、sorted等

和以前的Collection操作不同,Stream还有两个基础特征:

  • Pipelining:中间操作都会返回流对象本身。这样多个操作可以串联成一个管道。
  • 内部迭代:以前对集合遍历都是通过Iterator或者For-Each的方式,显示的在集合外部进行迭代。Stream提供了内部迭代的方式,通过访问者模式(Visitor)实现。

生成流

在Java8中,集合接口有两个方法生成流:

  • stream() : 为集合创建串行流。
  • parallelStream() - 为集合创建并行流。

这里要讲一下这两者的区别:

通俗点来说,一个是单线程、另一个是多线程。parallelStream是一个并行执行的流,通过默认的ForkJoinPool,可以提高多线程任务的速度。处理的过程会分而质之,将一个大任务切分成多个小任务,然后将结果合起来。(当然这也是一把双刃剑,在多线程的情况下,数据的并发访问需要关注,这也可以好好学一下,等之后再看吧~)


forEach

以前都是显示的使用循环,例如对一个集合进行打印: JDK7以前

for (int i = 0; i < list.size(); i++) {
    String temp = list.get(i);
    System.out.print(temp);
}

// 或者语法糖形式
for (String temp : list) {
    System.out.print(temp);
}

JDK8之后

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

对比一下,发现只需要一行代码就能实现~ 如果想在forEach中进行自定义的操作,可以创建一个类,实现Consumer函数接口,传递进去使用~


map

map方法用于映射每个元素到对应的结果:

Car car1 = Car.create(Car::new, "小汽车1");
        Car car2 = Car.create(Car::new, "小汽车2");
        List<Car> carList = Lists.newArrayList(car1, car2);

        List<String> nameList = carList.stream().map(Car::getName).collect(Collectors.toList());
        // 还可以加入filter功能,先进行过滤,然后取出想要的字段组成新的列表

Car类:

public class Car {
    
    private Integer id;

    private String name;

    public static Car create(final Supplier<Car> supplier, String name) {
        Car car = supplier.get();
        car.setName(name);
        return car;
    }

    public static void collide(final Car car) {
        System.out.println("Collided " + car.toString());
    }

    public void follow(final Car another) {
        System.out.println("Following the " + another.toString());
    }

    public void repair() {
        System.out.println("Repaired " + this.toString());
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "Car{}" + Thread.currentThread().getId() + getName();
    }
}


filter

如其名,filter就是用来过滤的,根据特定的条件来过滤元素:

List<Integer> ints = Lists.newArrayList(1, 5, 6, 7, 10, 20, 31, 8, 9);

        long count = ints.stream().filter(number -> number > 10).count();
        System.out.println(count);

上面的例子是用来过滤出整数列表中,值大于10的数量。


limit

用来获取指定数量的流(类似于MySQL中的limit):

public void limitTest() {
        List<Integer> ints = Lists.newArrayList(1, 5, 6, 7, 10, 20, 31, 8, 9);
        ints.stream().limit(5).forEach(System.out::println);
    }

sorted

用来对流进行排序,类似于Collections.sort(list, Comparator);

List<Integer> ints = Lists.newArrayList(1, 5, 6, 7, 10, 20, 31, 8, 9);
        ints.sort(Comparator.naturalOrder());

        Car car1 = Car.create(Car::new, "小汽车1");
        Car car2 = Car.create(Car::new, "小汽车2");
        List<Car> carList = Lists.newArrayList(car1, car2);
        // 可以自定义排序的字段,如果需要更复杂的排序规则,可以在lambda中的statement进行编写
        carList.sort(Comparator.comparing(Car::getName));

Collectors

Collectors类实现了很多规约操作,例如将流转换成集合和聚合元素。(一般用Collectors.toList()就够了)

//例如在一组对象中或者主键ID的列表可以这样写
public void collectorsTest() {
        List<Car> carList = Lists.newArrayList();
        // 省略构建参数,获取列表中的主键ID列表
        List<Integer> ids = carList.stream().map(Car::getId).collect(Collectors.toList());
    }


统计statistics

这个功能比较少用,还是学习记录一下吧。

public void statisticsTest() {
        List<Integer> ints = Lists.newArrayList(1, 5, 6, 7, 10, 20, 31, 8, 9);
        IntSummaryStatistics statistics = ints.parallelStream().mapToInt(x -> (int) x).summaryStatistics();
        System.out.println("最大值 : " + statistics.getMax());
        System.out.println("最小值 :" + statistics.getMin());
        System.out.println("平均值 :" + statistics.getAverage());
        System.out.println("总和 : " + statistics.getSum());
        System.out.println("数量 :" + statistics.getCount());
    }

完整的测试代码

package com.example.demo;

import com.example.demo.test.Car;
import com.google.common.collect.Lists;
import org.junit.Test;

import java.util.Arrays;
import java.util.Comparator;
import java.util.IntSummaryStatistics;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @author JingQ at 2019/1/31
 */
public class JDK8StreamTest {

    @Test
    public void buildStreamTest() {
        String content = "hello,world,debug,the,world";
        List<String> list = Arrays.asList(content.split(","));

        List<String> filter = list.stream()
                .filter(temp -> temp.startsWith("w"))
                .map(temp -> temp.substring(0, 3))
                .collect(Collectors.toList());
        filter.forEach(System.out::print);
    }

    @Test
    public void forEachTest() {
        String content = "hello,world,debug,the,world";
        List<String> list = Arrays.asList(content.split(","));

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


    @Test
    public void mapTest() {

        Car car1 = Car.create(Car::new, "小汽车1");
        Car car2 = Car.create(Car::new, "小汽车2");
        List<Car> carList = Lists.newArrayList(car1, car2);

        List<String> nameList = carList.stream().map(Car::getName).collect(Collectors.toList());
        // 还可以加入filter功能,先进行过滤,然后取出想要的字段组成新的列表
    }

    /**
     * filter过滤
     */
    @Test
    public void filterTest() {
        List<Integer> ints = Lists.newArrayList(1, 5, 6, 7, 10, 20, 31, 8, 9);

        long count = ints.stream().filter(number -> number > 10).count();
        System.out.println(count);
    }

    /**
     * limit 限制数量
     */
    @Test
    public void limitTest() {
        List<Integer> ints = Lists.newArrayList(1, 5, 6, 7, 10, 20, 31, 8, 9);
        ints.stream().limit(5).forEach(System.out::println);
    }

    @Test
    public void sortedTest() {
        List<Integer> ints = Lists.newArrayList(1, 5, 6, 7, 10, 20, 31, 8, 9);
        ints.sort(Comparator.naturalOrder());

        Car car1 = Car.create(Car::new, "小汽车1");
        Car car2 = Car.create(Car::new, "小汽车2");
        List<Car> carList = Lists.newArrayList(car1, car2);
        // 可以自定义排序的字段,如果需要更复杂的排序规则,可以在lambda中的statement进行编写
        carList.sort(Comparator.comparing(Car::getName));
    }

    @Test
    public void collectorsTest() {
        List<Car> carList = Lists.newArrayList();
        // 省略构建参数,获取列表中的主键ID列表
        List<Integer> ids = carList.stream().map(Car::getId).collect(Collectors.toList());
    }

    @Test
    public void statisticsTest() {
        List<Integer> ints = Lists.newArrayList(1, 5, 6, 7, 10, 20, 31, 8, 9);
        IntSummaryStatistics statistics = ints.parallelStream().mapToInt(x -> (int) x).summaryStatistics();
        System.out.println("最大值 : " + statistics.getMax());
        System.out.println("最小值 :" + statistics.getMin());
        System.out.println("平均值 :" + statistics.getAverage());
        System.out.println("总和 : " + statistics.getSum());
        System.out.println("数量 :" + statistics.getCount());
    }
}



小结

JDK的流式处理真的是太方便了(虽然以前的代码可以显示的调用,但感觉使用更少的代码来实现更加优雅😁)


个人博客项目地址

希望各位帮忙点个star,给我加个小星星✨


参考资料

1、Java 8 Stream