java8新特性之Stream流

一、什么是Stream流

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

集合讲的是数据,流讲的是计算

注意:

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

二、Stream操作的三个步骤

1、创建Stream

一个数据源,获取一个流

package com.xuzhi.stream;

/**
 * Author: 徐志
 * Date: 2020/8/1 15:20
 */

import com.xuzhi.entity.Employee;
import org.junit.Test;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Stream;

/**
 * Stream操作的三个步骤
 * 1.创建Stream
 * 2.中间操作
 * 3.终止操作
 */
public class TestStreamAPI1 {
    //1.创建Stream的几种方式
    @Test
    public void test1(){
        //1.可以通过Collection系列集合提供的stream()或者parallelStream()
        List<String> list=new ArrayList<>();
        Stream<String> stream1=list.stream();

        //2.通过Arrays中的静态方法获取数组流
        Employee[] emps=new Employee[10];
        Stream<Employee> stream2= Arrays.stream(emps);

        //3.通过Stream中的静态方法of()创建
        Stream<String> stream3 = Stream.of("aa", "dd", "dd", "ddddd");

        //4.创建无限流
        Stream<Integer> stream4 = Stream.iterate(0, (x) -> x + 2);
        stream4.limit(10)
                .forEach(System.out::println);

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

    }
}

2、中间操作

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

package com.xuzhi.stream;

/**
 * Author: 徐志
 * Date: 2020/8/1 15:33
 */

import com.xuzhi.entity.Employee;
import org.junit.Test;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.stream.Stream;

/**
 * 中间操作
 * 筛选和切片
 * filter--接收Lambda,从流中排出某些元素
 * limit--截断流,使其元素不超过给定 的数量
 * skip(n)--跳过元素,返回一个扔掉了前n个元素的流,若流中的元素不足n个,则返回一个空流
 * distinct---筛选,通过流所产生的hashCode和equals()去除重复元素
 */
public class TestStreamAPI2 {
    List<Employee> employees= Arrays.asList(
            new Employee("张三",18,9999.99),
            new Employee("李四",38,5555.99),
            new Employee("王五",50,6666.99),
            new Employee("赵六",16,3333.99),
            new Employee("田七",8,7777.99)
    );

    //内部迭代
    @Test
    public void test1(){
         employees.stream()
                 .filter((e)->e.getAge()>35)
                 .forEach(System.out::println);
    }

    //外部迭代
    @Test
    public void test2(){
        Iterator<Employee> it=employees.iterator();
        while (it.hasNext()){
            System.out.println(it.next());
        }
    }

    @Test
    public void test3(){
        employees.stream()
                .filter((e)->e.getSalary()>5000)
                .limit(2)
                .forEach(System.out::println);
    }

    @Test
    public void test4(){
        employees.stream()
                .filter((e)->e.getSalary()>5000)
                .skip(2)
                .forEach(System.out::println);
    }

    /**
     * 映射
     * map--接收lambda,将元素转换成其他形式获取信息。接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素
     * flatMap--接收一个函数作为参数,将流中的每一个值换成另一个流,然后把所有的流连成一个流
     *
     */
     @Test
    public void test5(){
         List<String> list=Arrays.asList("aaa","bbb","ccccc","ddd","ee");

         list.stream()
                 .map((str)->str.toUpperCase())
                 .forEach(System.out::println);

         employees.stream()
                 .map(Employee::getName)
                 .forEach(System.out::println);


         /**--------------------------------------------**/
         Stream<Stream<Character>> stream=list.stream()
                 .map(TestStreamAPI2::filterCharacter);

         stream.forEach((sm)->{
             sm.forEach(System.out::println);
         });

         /**--------------------------------------------**/
         list.stream()
                 .flatMap(TestStreamAPI2::filterCharacter)
                 .forEach(System.out::println);



     }

     public static Stream<Character> filterCharacter(String str){
         List<Character> list=new ArrayList<>();

         for (Character c:str.toCharArray()) {
             list.add(c);
         }
         return list.stream();
     }

    /**
     * 排序
     * sorted()--自然排序
     * sorted(Comparator com)
     *
     */
    @Test
    public void test7(){
        List<String> list=Arrays.asList("aa","ddd","dddf","rfgg");

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

        employees.stream()
                .sorted((e1,e2)->{
                    if(e1.getAge()==e2.getAge()){
                        return e1.getName().compareTo(e2.getName());
                    }else{
                        return Integer.compare(e1.getAge(),e2.getAge());
                    }

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

map和flatMap的区别
在讲解mapflatMap的区别之前,先来看看集合中add()和addALL()方法的区别

@Test
    public void test(){
        List list1=new ArrayList();
        List list2=new ArrayList();
        List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);

        list1.add(1);
        list1.add(2);
        list1.add(list);
        System.out.println(list1.toString());

        list2.add(1);
        list2.add(2);
        list2.addAll(list);
        System.out.println(list2.toString());
    }

运行结果
在这里插入图片描述
使用add()方法,他会将集合当做元素插入进集合当中,而addAll()而是将此集合中的元素插入。

map和flatMap与之类似,遇到同样的情况,map会返回一个Stream的集合,集合里面又有一个Stream的集合,二flatMap不会

3、终止操作

一个终止的操作,执行中间操作链,并产生结果

package com.xuzhi.stream;

/**
 * Author: 徐志
 * Date: 2020/8/2 8:11
 */

import com.xuzhi.entity.Employee;
import org.junit.Test;

import java.util.*;
import java.util.stream.Collectors;

/**
 * 终止操作
 */
public class TestStreamAPI3 {
    List<Employee> employees= Arrays.asList(
            new Employee("张三",18,9999.99),
            new Employee("李四",38,5555.99),
            new Employee("王五",50,6666.99),
            new Employee("赵六",16,3333.99),
            new Employee("田七",18,7777.99)
    );

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

    @Test
    public void  test1(){
        boolean b1 = employees.stream()
                .allMatch((e) -> e.getAge() == 18);
        System.out.println(b1);

        boolean b2 = employees.stream()
                .anyMatch((e) -> e.getAge() == 18);
        System.out.println(b2);

        boolean b3 = employees.stream()
                .noneMatch((e) -> e.getAge() == 18);
        System.out.println(b3);

        Optional<Employee> first = employees.stream()
                .sorted((e1,e2)->Double.compare(e1.getSalary(),e2.getSalary()))
                .findFirst();
        System.out.println(first.get());

        Optional<Employee> any = employees.stream()
                .sorted()
                .findAny();
        System.out.println(any.get());


    }

    @Test
    public void test2(){
        long count = employees.stream()
                .count();
        System.out.println(count);

        Optional<Employee> max = employees.stream()
                .max((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary()));
        System.out.println(max.get());

        Optional<Double> min = employees.stream()
                .map(Employee::getSalary)
                .min(Double::compare);
        System.out.println(min.get());
    }

    /**
     * 规约
     * reduce--可以将流中的元素反复结合起来,得到一个值
     */

    @Test
    public void test3(){
        List<Integer> list=Arrays.asList(1,2,3,4,5,6,7,8,9,0);

        Integer reduce = list.stream()
                .reduce(0, (x, y) -> x + y);
        System.out.println(reduce);

        Optional<Double> reduce1 = employees.stream()
                .map(Employee::getSalary)
                .reduce(Double::sum);

    }

    /**
     * 收集
     *
     * collect--将流转换成其他形式,接收一个Collector接口的实现,用于给Stream中元素做汇总的方法
     */
    @Test
    public void test4(){
        List<String> collect = employees.stream()
                .map(Employee::getName)
                .collect(Collectors.toList());
        collect.forEach(System.out::println);

        Set<Double> collect1 = employees.stream()
                .map(Employee::getSalary)
                .collect(Collectors.toSet());
        collect1.forEach(System.out::println);

        HashSet<String> collect2 = employees.stream()
                .map(Employee::getName)
                .collect(Collectors.toCollection(HashSet::new));
        collect2.forEach(System.out::println);
    }

    @Test
    public void test5(){
        Long collect = employees.stream()
                .collect(Collectors.counting());
        System.out.println(collect);

        Double collect1 = employees.stream()
                .collect(Collectors.averagingDouble(Employee::getSalary));
        System.out.println(collect1);

       Double collect2 = employees.stream()
                .collect(Collectors.summingDouble(Employee::getSalary));
        System.out.println(collect2);

        Optional<Employee> collect3 = employees.stream()
                .collect(Collectors.maxBy((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary())));
        System.out.println(collect3.get());

    }

    //分组
    @Test
    public void test6(){
        Map<Integer, List<Employee>> collect = employees.stream()
                .collect(Collectors.groupingBy(Employee::getAge));
        System.out.println(collect);
    }
    

   //分区
   @Test
    public void test8(){
       Map<Boolean, List<Employee>> collect = employees.stream()
               .collect(Collectors.partitioningBy((e) -> e.getSalary() > 6000));
       System.out.println(collect);
   }

   @Test
   public void test9(){
       DoubleSummaryStatistics collect = employees.stream()
               .collect(Collectors.summarizingDouble(Employee::getSalary));
       System.out.println(collect.getMax());
   }

   //连接
   @Test
    public void test10(){
       String collect = employees.stream()
               .map(Employee::getName)
               .collect(Collectors.joining(","));
       System.out.println(collect);
   }
}

三、小练习

package com.xuzhi.stream;

import com.xuzhi.entity.Employee;
import org.junit.Test;

import java.util.Arrays;
import java.util.List;
import java.util.Optional;

/**
 * Author: 徐志
 * Date: 2020/8/2 10:33
 */
public class Zuoye {
    /**
     * 给定一个数字列表,如何返回一个由每个数的平方组成的列表
     * 给定【1,2,3,4,5】,应该返回【1,4,9,16,25】
     */

    @Test
    public void test1(){
        Integer[] nums=new Integer[]{1,2,3,4,5};
        Arrays.stream(nums)
                .map((x)->x*x)
                .forEach(System.out::println);
    }

    /**
     * 怎样用map+reduce数出有多少个Employee
     */
    List<Employee> employees= Arrays.asList(
            new Employee("张三",18,9999.99),
            new Employee("李四",38,5555.99),
            new Employee("王五",50,6666.99),
            new Employee("赵六",16,3333.99),
            new Employee("田七",8,7777.99)
    );
    public void test2(){
        Optional<Integer> reduce = employees.stream()
                .map((e) -> 1)
                .reduce(Integer::sum);
        System.out.println(reduce.get());
    }

}

四、并行流和顺序流

并行流就是把一个内容分成多个数据块,并用不同的线程分别处理每个数据块的流

java8中将并行进行了优化,我们可以很容易的对数据进行并行操作,StreamAPI可以声明性地通过parallel()与sequential()在并行流与顺序流之间进行切换
在这里插入图片描述
在这里插入图片描述

  • ForkJoinCalculate.java
package com.xuzhi.forkJoin;

import java.util.concurrent.RecursiveTask;

/**
 * Author: 徐志
 * Date: 2020/8/2 11:20
 */
 /**
 使用ForkJoin框架必须继承RecursiveAction或者RecursiveTask。两者的区别是RecursiveTask有返回值,RecursiveAction没有返回值
 */
public class ForkJoinCalculate extends RecursiveTask<Long> {

    private static final long serialVersionUID=233224555L;

    private long start;
    private long end;

    private static final long THREADHOLD=10000;

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

    }
    @Override
    protected Long compute() {
        long length=end-start;
        if(length<=THREADHOLD){
            long sum=0;
            for (long i=start;i<=end;i++){
                sum+=i;
            }
            return sum;

        }else{
            long mid=(start+end)/2;
            ForkJoinCalculate left=new ForkJoinCalculate(start,mid);
            left.fork();
            ForkJoinCalculate right=new ForkJoinCalculate(mid+1,end);
            right.fork();

            return left.join()+right.join();
        }
    }
}

  • TestForkJoin.java
package com.xuzhi.forkJoin;

import org.junit.Test;

import java.time.Duration;
import java.time.Instant;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.ForkJoinTask;
import java.util.stream.LongStream;

/**
 * Author: 徐志
 * Date: 2020/8/2 11:29
 */
public class TestForkJoin {

/*
使用ForkJoin
*/
    @Test
    public void test1(){
        Instant startTime = Instant.now();

        ForkJoinPool pool=new ForkJoinPool();
        ForkJoinTask<Long> task=new ForkJoinCalculate(0,100000000L);
        Long sum=pool.invoke(task);
        System.out.println(sum);

        Instant endTime = Instant.now();
        System.out.println(Duration.between(startTime, endTime).toMillis());
    }

/*
传统的for循环
*/
    @Test
    public void test2(){

        Instant startTime = Instant.now();
        long sum=0L;
        for (long i = 0; i <=100000000L; i++) {
            sum+=i;
        }
        System.out.println(sum);
        Instant endTime = Instant.now();
        System.out.println(Duration.between(startTime, endTime).toMillis());


    }

    //java8并行流
    public void test3(){
        Instant startTime = Instant.now();
        LongStream.rangeClosed(0,100000000L)
                .parallel()
                .reduce(0,Long::sum);

        Instant endTime = Instant.now();
        System.out.println(Duration.between(startTime, endTime).toMillis());

    }
}

使用forkJoin框架或者并行流都是为了充分利用CPU的资源,当数据量过小的时候,for循环会比其他两种方式块,当数据量较大时,并行流的优势就提现出来了

五、Optional

Optional<T>类是一个容器类,代表一个值存在或者不存在,原来使用Null表示一个值不存在,现在Optional可以更好的表达这个概念,并且可以避免空指针异常

常用方法:

  • Optional.of(T t):创建一个Optional实例
  • Optional.empty():创建一个空的Optional实例
  • Optional.ofNullable(T t):若t不为null,创建Optional实例,否则则创建空实例
  • isPresent():判断是否包含值
  • orElse(T t):如果调用 的对象包含值,返回该值,否则返回t
  • orElseGet(Supplier s):如果调用对象包含值,返回该值,否则返回s获取的值
  • map(Function f):如果有值对其处理,并返回处理后的Optional,否则返回Optional.empty()
  • flatMap(Function mapper):与map类似,要求返回值必须是Optional
package com.xuzhi.optional;

import com.xuzhi.entity.Employee;
import org.junit.Test;

import java.util.Optional;

/**
 * Author: 徐志
 * Date: 2020/8/2 14:53
 */
public class TestOptional {

    @Test
    public void test1(){
        //不能为null
        Optional<Employee> op=Optional.of(new Employee());

        Employee employee = op.get();
        System.out.println(employee);
    }

    @Test
    public void test2(){
        Optional<Employee> op = Optional.empty();
        System.out.println(op.get());

    }

    @Test
    public void test3(){
        //有值就获取,没值就什么都不做
        Optional<Employee> op = Optional.ofNullable(null);
       /* if (op.isPresent()){
            System.out.println(op.get());
        }*/

        Employee employee = op.orElse(new Employee("李四", 66, 99999));
        System.out.println(employee);

        Employee employee1 = op.orElseGet(() -> new Employee());
        System.out.println(employee1);


    }

    @Test
    public void test4(){
        Optional<Employee> dd = Optional.ofNullable(new Employee("dd", 33, 4343));
      /*  Optional<String> s = dd.map((e) -> e.getName());
        System.out.println(s.get());*/

        Optional<String> s = dd.flatMap((e) -> Optional.of(e.getName()));
        System.out.println(s);

    }
}

六、总结

本人观看的学习资料为B站尚硅谷教学视频。简单的学习了解了Stream流操作集合的强大,过去对集合中的元素进行操作可能需要写比较复杂的代码,现在通过Stream流配合lambda表达式,实现一些功能实在是太轻松了。附上本人微信公众号二维码,欢迎一起交流学习
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值