Stream 流

1、概念

Stream是Java8 中处理集合的关键抽象概念,它可以指定你希望对集合进行的操作,可以执行非常复杂的查找、过滤和映射数据等操作。使用Stream API对集合数据进行操作,就类似于使用SQL 执行的数据库查询。也可以使用Stream API来并行执行操作。简而言之,Stream API提供了一-种高效且易于使用的处理数据的方式。

①Stream自己不会存储元素
②Stream不会改变源对象。相反,他们会返回一个持有结果的新Stream
③Stream操作是延迟执行的。这意味着他们会等到需要结果的时候才执行

Stream 流_第1张图片

2、创建流

通过Collection系列集合提供的stream()或parallelStream( )

    @Test
    public void test1(){
        List list=new ArrayList<>();
        Stream stringStream=list.stream();
    }

通过Arrays中的静态方法stream( )获取数组流

    @Test
    public void test2(){
        Cat[] cats=new Cat[]{};
        Stream catStream= Arrays.stream(cats);
    }

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

    @Test
    public void test3(){
        Stream stringStream=Stream.of("a","b","c");
    }

创建无限流

    @Test
    public void test4(){
        Stream integerStream=Stream.iterate(0,x->x+2);
        integerStream.limit(10).forEach(System.out::println);
    }

3、中间操作

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

筛选和切片:

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

举个栗子

public class Cat {

    int age;
    String name;

    public Cat(){
    }

    public Cat(int age){
        this.age=age;
    }

    public Cat(int age,String name){
        this.age=age;
        this.name=name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getName() {
        return name;
    }

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

    @Override
    public String toString() {
        return "Cat{" +
                "age=" + age +
                ", name='" + name + '\'' +
                '}';
    }
}

    @Test
    public void testStream(){
        List list=Arrays.asList(
                new Cat(2,"Kitty"),
                new Cat(3,"Garfield"),
                new Cat(5,"Tom")
        );

      list.stream() // 创建流
                .filter(x->x.getAge()>2) // 中间操作
                .forEach(System.out::println); // 终止操作

    }

结果

Cat{age=3, name='Garfield'}
Cat{age=5, name='Tom'}

映射:

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

举个栗子

    @Test
    public void testStreamMap(){
        Stream stringStream=Stream.of("aaa","bbb","ccc");
        stringStream.map(x->x.toUpperCase())
                .forEach(System.out::println);
    }

输出结果为

AAA
BBB
CCC

再举个栗子

    @Test
    public void testStreamMap2(){
        List list=Arrays.asList(
                new Cat(2,"Kitty"),
                new Cat(3,"Garfield"),
                new Cat(5,"Tom")
        );

       list.stream().map(Cat::getName).forEach(System.out::println);
    }

输出结果为

Kitty
Garfield
Tom

排序:

sorted()-自然排序(Comparable);
sorted(Comparator com)- 定制排序(Comparator)。

查找与匹配:

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

4、并行流和串行流

并行流就是把一个内容分成多个数据块,并用不同的线程分别处理每个数据块的流。
java8对并行流进行了一些优化,方便用户很容易的对数据进行并行操作,Stream API 可以声明性地通过 parallel() 与 sequential() 在并行流与顺序流之间进行切换。

从JDK1.7开始,Java提供了ForkJoin框架用于并行地执行任务,它的思想就是将一大大的任务拆分成若干个小任务,最终汇总每个小任务的结果得到这个大任务的结果。
Stream 流_第2张图片
举个栗子:假设现在我们需要对0到50000000000进行求和

栗子1:使用for循环进行单线程操作,CPU利用率低,耗时42911ms

    @Test
    public void test1() {
        Instant start = Instant.now();
        long sum = 0L;
        for (long i = 0; i <= 50000000000L; i++) {
            sum += i;
        }
        System.out.println(sum);
        Instant end = Instant.now();
        System.out.println("耗费时间为: " + Duration.between(start, end).toMillis());
    }

Stream 流_第3张图片
栗子2:使用 ForkJoin 进行任务拆分,CPU利用率100%,耗时25225ms

public class ForkJoinCalculate extends RecursiveTask {
    private long start;
    private long end;

    private static final long THRESHOLD = 10000;

    public ForkJoinCalculate(long start, long end) {
        this.start = start;
        this.end = end;
    }

    /**
     * 运算或者 任务拆分
     * @return
     */
    @Override
    protected Long compute() {
        long length = end - start;
        if (length <= THRESHOLD) {
            //当前任务数据区间少于10000
            long sum = 0;
            for (long i = start; i <= end; i++) {
                sum += i;
            }
            return sum;
        } else {
            //当前任务数据区间大于10000   进行任务拆分
            long middle = (start + end) / 2;
            ForkJoinCalculate left = new ForkJoinCalculate(start, middle);
            ForkJoinCalculate right = new ForkJoinCalculate(middle + 1, end);
            left.fork();
            right.fork();
            return left.join() + right.join();
        }
    }
}

    @Test 
    public void test2() {
        Instant start = Instant.now();
        ForkJoinPool fjp = new ForkJoinPool( );
        ForkJoinTask task = new ForkJoinCalculate( 0 , 50000000000L ) ;
        Long sum = fjp.invoke(task);
        System.out.println(sum);
        Instant end = Instant.now();

        System.out.println( "耗费时间为。"+Duration.between(start, end). toMillis());
    }

Stream 流_第4张图片
栗子3:使用Stream的并行流,使用起来更加简单,也可以充分的利用CPU

    @Test
    public void test3(){
        Instant start = Instant . now();
        long res = LongStream.rangeClosed(0, 1000000L).
        parallel().reduce(0,Long::sum) ;
        System.out.println(res);
        Instant end = Instant.now();
        System.out.println( "耗费时间为: "+Duration. between(start, end). toMillis());
    }

Stream 流_第5张图片
其实并行流的的底层使用的也是 ForkJoin 框架
Stream 流_第6张图片
Stream 流_第7张图片
Stream 流_第8张图片

你可能感兴趣的:(Java8,新特性)