什么是Stream?

Stream是JDK8 API的新成员,它允许以声明性方式处理数据集合

特点

  1. 代码简洁: 函数式编程写出的代码简洁且意图明确,使用stream接口让你从此告别for循环
  2. 多核友好: Java函数式编程使得编写并行程序从未如此简单,你需要的全部就是调用一下方法

为什么要使用Stream API?

  • 实际开发中,项目中多数数据源都来自于MySQL,Oracle等.但现在数据源可以更多了,有MongoDB,Redis等,而这些NoSQL的数据就需要Java层面去处理
  • Stream和Collection集合的区别:Collection是一种静态的内存数据结构,而Stream是有关计算的.前者是主要面向内存,存储在内存中,后者主要是面向CPU,通过CPU实现计算
    注意:
  1. Stream自己不会存储元素
  2. Stream不会改变源对象.相反,他们会返回一个持有结果的新Stream
  3. Stream操作是延迟执行的.这意味着他们会等到需要结果的时候才执行

步骤

  1. 创建Stream
    一个数据源(集合,数组等),获取一个流
  2. 中间操作
    一个中间操作链,对数据源的数据进行处理
  3. 终止操作
    一旦执行终止操作,就执行中间操作链,并产生结果.之后,不会再被使用

Java的stream流获取最大时间_Test

Stream的实例化

/**
 * @PROJECT_NAME: myTest
 * @DESCRIPTION:  Stream的方法测试
 * @USER: 罗龙达
 * @DATE: 2021/2/21 2:33
 */
public class streamTest {

    @Test
    public void createStream(){
        List<String> strings = Arrays.asList("abc", "", "bc", "efg", "abcd", "", "jk");
        //1. 通过集合创建stream   --  返回一个顺序流
        Stream<String> listStream = strings.stream();

        //返回一个并行流
        Stream<String> paraStream = strings.parallelStream();

        //2. 通过数组创建stream
        IntStream arrStream = Arrays.stream(new int[]{1, 3, 45, 6, 877, 12});

        //3. 通过Stream的of()方法
        Stream<Integer> integerStream = Stream.of(1, 3, 5, 7, 9);

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

        //5. 生成
        Stream.generate(Math::random).limit(10).forEach(System.out::println);
    }
}

中间操作的部分API测试

@Test
    public void InOperationTest() {
        List<String> strings = Arrays.asList("abc", "", "bc", "efg", "abcd", "abc", "jk");
        //1. 通过集合创建stream   --  返回一个顺序流
        Stream<String> listStream = strings.stream();
        System.out.println("-----------测试filter--------------");
        /**
         *  filter(Predicate predicate) -- 接收lambda,从流中排除某些元素
         *  查询列表中不为null的元素
         */
        //
        listStream.filter(s -> !s.equals("")).forEach(System.out::println);
        System.out.println("--------------测试limit------------");
        /**
         *limit(n)  -- 截断流,使元素不超过给定数量
        */
        strings.stream().limit(3).forEach(System.out::println);

        System.out.println("-------------测试skip--------------");
        /**
         *skip(n)  --  跳过元素,返回一个扔掉了前n个元素的流,如果流中元素不足n个,则返回一个空流
         */
        strings.stream().skip(3).forEach(System.out::println);

        System.out.println("------------测试distinct-----------");
        /**
         *distinct()  --  筛选,通过流所产生元素的hashCode()和equals()去除重复元素
         */
        strings.stream().distinct().forEach(System.out::println);
    }

Map映射API的部分测试

@Test
    public void mapTest(){
        System.out.println("----------------将字符串转换成大写---------------");
        List<String> strings = Arrays.asList("aaa", "bb", "cccc", "d","eeeee","ff");
        /**
         * 接受一个函数作为参数,将元素转换成其他形式或提取信息,该函数会被应用到每个元素上,并将其映射成一个新的元素
         */
        strings.stream().map(String::toUpperCase).forEach(System.out::println);
        System.out.println("------------字符串长度大于3的元素的大写-----------");

        /**
         * 返回字符串长度大于3的元素的大写
         */
        strings.stream().filter(s -> s.length() >3).map(String::toUpperCase).forEach(System.out::println);

        /**
         * flatMap(Function f) -- 接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流。
         */
        strings.stream().flatMap(streamTest::fromStringToStream).forEach(System.out::println);
    }

    //将字符串中的多个字符构成的集合转换为对应的流
    public static Stream<Character> fromStringToStream(String s){
        ArrayList<Character> list = new ArrayList<>();
        for (char c : s.toCharArray()) {
            list.add(c);
        }
        return list.stream();
    }

map与flatMap的区别

  • 从上面例子可以看出,map 生成的是个 1:1 映射,每个输入元素,都按照规则转换成为另外一个元素。还有一些场景,是一对多映射关系的,这时需要 flatMap。
  • flatMap 把 inpuStream 中的层级结构扁平化,就是将最底层元素抽出来放到一起,最终 output 的新 Stream 里面已经没有 List 了,都是直接的数字。

排序API测试

@Test
    public void sortTest() {
        List<Integer> list = Arrays.asList(23, 45, -12, 2, 7, 89, 5);
        System.out.println("----------从小到大排序测试---------");
        list.stream().sorted().forEach(System.out::println);
        System.out.println("----------从大到小排序测试---------");
        list.stream().sorted((i1, i2) -> i2 - i1).forEach(System.out::println);
    }

终止操作的部分API测试

@Test
    public void termOperationTest(){
        List<Integer> list = Arrays.asList(23, 45, -12, 2, 7, 89, 5);
        System.out.println("-----------allMatchTest-------------");
        /**
         * allMatch(Predicate predicate)  --  检查是否匹配所有元素
         */
        boolean allMatch = list.stream().allMatch(integer -> integer > 0);
        System.out.println(allMatch);

        System.out.println("-----------anyMatchTest-------------");
        /**
         * allMatch(Predicate predicate)  --  检查是否匹配所有元素
         */
        boolean anyMatch = list.stream().anyMatch(integer -> integer > 0);
        System.out.println(anyMatch);

        System.out.println("----------noneMatchTest-------------");
        /**
         * noneMatch(Predicate predicate)  --  检查是否没有匹配的元素
         */
        boolean noneMatch = list.stream().noneMatch(integer -> integer > 0);
        System.out.println(noneMatch);

        System.out.println("----------findFirstTest-------------");
        /**
         * findFirst()  --  返回流中第一个元素
         */
        Optional<Integer> first = list.stream().findFirst();
        System.out.println(first);
        System.out.println("------------findAnyTest-------------");
        /**
         * findAny()  --  返回流中任一元素
         */
        Optional<Integer> any = list.parallelStream().findAny();
        System.out.println(any);

        System.out.println("--------------countTest-------------");
        /**
         * count()  --  返回流中元素的总个数
         */
        long count = list.stream().filter(i -> i > 0).count();
        System.out.println(count);

        System.out.println("------------max / min Test----------");
        /**
         * max/min(Comparator comparator)  --  返回流中最大值 / 最小值
         */
        Optional<Integer> max = list.stream().max((i1, i2) -> i1 - i2);
        System.out.println("max = " + max);
        Optional<Integer> min = list.stream().min((i1, i2) -> i1 - i2);
        System.out.println("min = " + min);

        System.out.println("--------------forEachTest------------");
        /**
         * forEach(Consumer c)  --  内部迭代
         */
//        list.stream().forEach(System.out::println);
        list.forEach(System.out::println);
    }

规约操作部分API测试

@Test
    public void reduceTest(){
        List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
        /**
         * reduce(T identity, BinaryOperator) -- 可以将流中元素反复结合起来,得到一个值
         */
        Integer reduce = list.stream().reduce(0, Integer::sum);
        System.out.println("reduce = " + reduce);

        /**
         * reduce(BinaryOperator)  -- 可以将流中元素反复结合起来,得到一个值,返回optional
         */
        Optional<Integer> reduce2 = list.stream().reduce(Integer::sum);
        Optional<Integer> reduce3 = list.stream().reduce((i1, i2) -> i1 + i2);
        System.out.println("reduce2 = " + reduce2);
        System.out.println("reduce3 = " + reduce3);
    }

collect部分API测试

@Test
public void collectTest(){
    List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
    /**
     *collect(Collector c) 将流转换为其他形式.接受一个Collector接口的实现
     * 查找大于4的数字,结果返回一个list / set
     */
    List<Integer> collectList = list.stream().filter(i -> i > 4).collect(Collectors.toList());
    collectList.forEach(System.out::println);
    Set<Integer> collectSet = list.stream().filter(i -> i > 4).collect(Collectors.toSet());
    collectSet.forEach(System.out::println);

}

Optional类

  • 到目前为止,臭名昭著的空指针异常是导致Java应用程序失败的最常见原因.
    以前,为了解决空指针异常,Google公司著名的Guava项目引入了Optional类,Guava通过使用检查空值的方式来防止代码污染,鼓励程序员写更干净的代码.受到Google Guava的启发,Optional类已经成为Java 8 类库的一部分
  • Optional类是一个容器类,他可以保存类型T的值,代表这个值存在,或者仅仅保存null,表示这个值不存在.原来用null表示一个值不存在,现在Optional可以更好地表达这个概念.并且可以避免空指针异常
  • Optional类的Javadoc描述如下: 这是一个可以为null的容器对象.如果值存在在isPresent()方法会返回true,调用get()方法会返回该对象

相关方法

# 1 创建Optional类对象的方法
- Optional.of(T t): 创建一个Optional实例,`t必须非空`
- Optional.empty(): 创建一个空的Optional实例
- Optional.ofNullable(T t): t可以为null

# 2 判断Optional容器中是否包含对象: 
- boolean isPresent(): 判断是否包含对象
- void ifPresent(Consumer consumer): 如果有值,就执行Consumer接口的实现代码,并且该值会作为参数传给它

# 3 获取Optional容器的对象
- T get(): 如果调用对象包含值,返回该值,否则抛异常
- T orElse(): 如果有值则将其返回,否则返回指定的other对象
- T orElseGet(Supplier other): 如果有值将其返回,否则返回由Supplier接口实现提供的对象
- T orElseThrow(Supplier exceptionSupplier): 如果有值则将其返回,否则抛出由Supplier接口实现提供的异常

相关方法代码测试

  • Girl实体类
public class Girl {
    public Girl() {
    }

    public Girl(String name) {
        this.name = name;
    }

    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}
  • Boy实体类
public class Boy {
    private Girl girlFriend;
    private String name;

    public Boy() {
    }

    public Boy(Girl girlFriend, String name) {
        this.girlFriend = girlFriend;
        this.name = name;
    }

    public Girl getGirlFriend() {
        return girlFriend;
    }

    public void setGirlFriend(Girl girlFriend) {
        this.girlFriend = girlFriend;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}
@Test
    public void OptionalTest(){
        Girl girl = new Girl();
        girl=null;
        Optional<Girl> optionalGirl = Optional.ofNullable(girl);
        System.out.println("optionalGirl = " + optionalGirl);
    }

    //原始的getName方法
    public String getGirlName(Boy boy){
        return boy.getGirlFriend().getName();
    }

    //优化后的getName方法
    public String getGirlNameAfterOptimizing(Boy boy){
        Optional<Boy> boyOptional = Optional.ofNullable(boy);
        //此时boy1一定非空
        Boy boy1 = boyOptional.orElse(new Boy(new Girl("杨幂"), "我"));
        Girl girlFriend = boy1.getGirlFriend();
        Optional<Girl> girlFriend1 = Optional.ofNullable(girlFriend);
        //girlOption一定非空
        Girl girlOptional = girlFriend1.orElse(new Girl("赵丽颖"));


        return "女朋友的名字 : " + girlOptional.getName();
    }

    @Test
    public void testGetName(){
        Boy boy = new Boy();
//        boy = null;
        boy.setGirlFriend(null);
//        boy.setGirlFriend(new Girl("迪丽热巴"));
        System.out.println(getGirlNameAfterOptimizing(boy));
    }