Stream流和Optional

Lambda表达式和函数式接口https://blog.csdn.net/qq_45888932/article/details/122451124

目录

一、什么是Stream流

注意事项

二、快速体验

数据准备

数据跟踪

三、创建流

四、中间操作

 五、终结操作

查找和匹配

reduce

六、Optional

安全获取值


一、什么是Stream流

使用函数式编程,用来对集合或者数组进行链状流式的操作

注意事项

  1.  流中没有终结操作,中间操作也是不会执行的
  2. 流是一次性的,在终结操作之后这个流不可以使用了
  3. 流中的操作对于原数据没有影响

二、快速体验

数据准备

pom.xml中添加依赖,这个依赖用于减少代码,快速开发

    <dependency>
      <groupId>org.projectlombok</groupId>
      <artifactId>lombok</artifactId>
      <version>1.18.22</version>
    </dependency>

创建实体类

package com.righteye.domain;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;

import java.util.List;

@Data
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode // 用于去重
public class Author {

    private Long id;

    private String name;

    private Integer age;

    private String intro;

    private List<Book> books;

}
package com.righteye.domain;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;

@Data
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode // 用于去重
public class Book {

    private Long id;

    private String name;

    private Integer score;

    private String intro;
}

测试类

package com.righteye;

import com.righteye.domain.Author;
import com.righteye.domain.Book;

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

public class Demo01 {

    public static void main(String[] args) {

        // 删选出所有年龄大于18的作者,并对内容去重
        List<Author> authors = getAuthors();

        authors.stream()  // 将List转换成stream流
                .filter(author -> author.getAge() > 18)
                .forEach(author -> System.out.println(author));

    }

    private static List<Author> getAuthors() {
        Author author = new Author(1L, "青雉", 33, "海军大将", null);
        Author author1 = new Author(2L, "赤犬", 35, "海军大将", null);
        Author author2 = new Author(3L, "路飞", 18, "海贼", null);
        Author author3 = new Author(3L, "路飞", 18, "海贼", null);

        List<Book> books1 = new ArrayList<>();
        List<Book> books2 = new ArrayList<>();
        List<Book> books3 = new ArrayList<>();

        books1.add(new Book(1L, "冰与火", 88, "11111"));
        books1.add(new Book(2L, "冰与火2", 99, "111122221"));

        books2.add(new Book(3L, "冰2", 99, "111122221"));
        books2.add(new Book(4L, "火2", 91, "111122221"));
        books2.add(new Book(4L, "火2", 91, "111122221"));

        books3.add(new Book(5L, "海贼王", 91, "全集"));
        books3.add(new Book(6L, "革命家", 91, "111122221"));
        books3.add(new Book(6L, "革命家", 91, "111122221"));

        author.setBooks(books1);
        author1.setBooks(books2);
        author2.setBooks(books3);
        author3.setBooks(books3);

        return new ArrayList<>(Arrays.asList(author, author1, author2, author3));

    }

}

        stream中提供了方法可以对集合中的元素进行筛选,方法中的参数很多都是函数式接口,使用lambda表达式,初学的时候按照参数要求直接创建相应的匿名内部类,然后 alt + enter自动转化成lambda表达式。

数据跟踪

对上面的例子进行debug调试,idea中提供了对于stream的跟踪操作

 

 idea yyds,秒啊!

三、创建流

1.单列集合  集合.stream()

List<Author> authors = getAuthors();

authors.stream()  // 将List转换成stream流
        .filter(author -> author.getAge() > 18)
        .forEach(author -> System.out.println(author));

 2.数组  Arrays.stream(args) 或者 Stream.of(args) 的方式

Integer[] arr = {1, 2, 3, 4, 5};
Stream<Integer> stream = Arrays.stream(arr);
Stream<Integer> stream1 = Stream.of(arr);

3.双列集合 先转换成单列集合

Map<String, Integer> map = new HashMap<>();
map.put("user1", 1);
map.put("user2", 2);
map.put("user3", 3);

// 先使用entrySet转换成单列集合
Stream<Map.Entry<String, Integer>> stream2 = map.entrySet().stream();

四、中间操作

1.filter  对流中的数据进行过滤

// 对作者姓名长度 > 1的过滤
 authors.stream()
         .distinct()  // 去重
         .filter(author -> author.getName().length() > 1)
         .forEach(author -> System.out.println(author));

2.map  对流中的元素进行计算或转换

authors.stream()
     // 匿名内部类形式:Function第一个参数为集合中的元素类型,第二个参数为转换后流中的元素
      .map(new Function<Author, String>() {
           @Override
           public String apply(Author author) {
                  return author.getName();
           }
      })
      .forEach(str -> System.out.println(str));

lambda优化:

authors.stream()
        // 匿名内部类形式:Function第一个参数为集合中的元素类型,第二个参数为转换后流中的元素
        .map(author -> author.getName())
        .forEach(str -> System.out.println(str));

3.distinct   对流中元素进行去重;使用distinct需要重写equals方法

4.sorted    对流中元素进行排序

        当使用顺序排序的情况下,可以继续优化

// 对流中元素通过年龄降序排序,并进行去重
 authors.stream()
         .distinct()
         .sorted(Comparator.comparingInt(Author::getAge))
         .forEach(author -> System.out.println(author));

        这里的lambda我直接alt + enter生成的,待我在往下学学;我现在只会下面这个

 authors.stream()
        .distinct()
        .sorted((author1, author2) -> author1.getAge() - author2.getAge())
        .forEach(author -> System.out.println(author));

5.limit  限制流的长度,超出部分舍弃

 // 对流中元素通过年龄降序排序,并进行去重 只打印前两个人
authors.stream()
       .distinct()
       .sorted((a1, a2) -> a2.getAge() - a2.getAge())
       .limit(2)
       .forEach(author -> System.out.println(author));

7.skip  跳过流中前n个元素,返回剩下的

8.flatMap  

        map只能把一个对象转换成另一个对象作为流中的元素;而faltMap可以将一个对象转换成多个对象

authors.stream()
       .distinct()
       .flatMap(new Function<Author, Stream<?>>() {
            @Override
            public Stream<?> apply(Author author) {
                   return author.getBooks().stream();
            }
        })
       .forEach(book -> System.out.println(book));

 优化:

// 打印每名作者的书籍
authors.stream()
       .distinct()
       .flatMap((Function<Author, Stream<?>>) author -> author.getBooks().stream())
       .forEach(book -> System.out.println(book));

 debug追踪执行

 五、终结操作

        stream流中只有在最后调用终结操作,整个链式调用才能真正执行。

1.forEach  对流中的元素进行遍历。、

2.count  获取流中元素的个数

 // 获取所有作家的所有书籍总数
long count = authors.stream()
         .flatMap(author -> author.getBooks().stream())
         .distinct()
         .count();
 System.out.println(count);

count()没有参数,但是有个返回值接收结果

3.min&max  获取流中的最值

 // 获取年级最大的作者
 Optional<Author> max = authors.stream()
       .max(((o1, o2) -> o2.getAge() - o1.getAge()));
System.out.println(max.get());

4.collect  将流中的元素转换成集合

  • 转换成list
// 获取作者的名字并封装成list 
List<String> list = authors.stream()
        .distinct()
        .map(author -> author.getName())
        .collect(Collectors.toList());
System.out.println(list);

collect中的参数是Collector, 是个接口但不适合使用匿名内部类,这里使用工具类Collectors进行转换

  • 转换成map
 // 获取作者的名字和书籍并用map存储
final Map<String, List<Book>> authorMap = authors.stream()
      .distinct()
      .collect(Collectors.toMap(author -> author.getName(), author -> author.getBooks()));
for (Map.Entry<String, List<Book>> strName : authorMap.entrySet()) {
      System.out.println(strName.getKey() + " " + strName.getValue());
 }

Collectors.toMap中的两个参数表示要将流中的元素分别转换到key和value所对应的类型

查找和匹配

一种条件判断,类似filter,但是filter是中间操作,而下面的方法是终结操作

1.anyMatch   判断是否存在一条数据满足条件

 // 判断是否有年龄在44以上的人
boolean flag = authors.stream()
       .distinct()
       .anyMatch(author -> author.getAge() > 44);
System.out.println(flag);

使用终结方法后流就停止了,不会再继续向下传递

2.allMatch  判断是否所有的元素都满足条件

3.noneMatch   判断是否都不符合条件

4.findAny   随机获取流中的一个元素

Optional<Author> res = authors.stream()
        .findAny();
System.out.println(res.get());

5.findFirst   获取流中第一个元素

reduce

        用于将流中的元素通过定义的计算方法合并成一个结果并返回

        内部实现:

res = initial;
public Integer apply(Integer res, Integer number) {
    return (res, number)的具体计算关系;
}
res = apply(res, number);

 测试实例:

// 计算所有作者的年龄之和
Integer res = authors.stream()
          .distinct()
          .map(author -> author.getAge())  // 将流中的元素转换成后面要计算的类型元素
          // reduce 中第一个变量初始值,然后第二个参数是具体的实现逻辑
          .reduce(0, (res1, number) -> res1 + number);
System.out.println(res);

六、Optional

        Optional用来避免程序中可能出现的空指针异常

传统使用

 public static void main(String[] args) {
        String author = getAuthorName();
        if (author != null)
        System.out.println(author);
    }

    private static String getAuthorName() {
        Author author = new Author(1L, "青雉", 33, "海军大将", null);
        return null;
    }

使用Optional后可以完全不考虑空指针的问题

public static void main(String[] args) {
     Optional<Author> author = getAuthorName();
     author.ifPresent(name -> System.out.println(author.get().getName()));
}

private static Optional<Author> getAuthorName() {
     Author author = new Author(1L, "青雉", 33, "海军大将", null);
     return Optional.ofNullable(author);
}

实际上,在mybatis 3.5版本以上可以将返回的数据封装成Optional,这样可以省略自己封装的步骤,直接调用。

安全获取值

使用orElseGet()方法可以避免空指针异常的获取对象属性

public static void main(String[] args) {

    Optional<Author> author = getAuthorName();
    Author res = author.orElseGet(() -> new Author(1L, "赤犬", 33, "海军大将", null));
    System.out.println(res.getName());
}


private static Optional<Author> getAuthorName() {
    Author author = new Author(1L, "青雉", 33, "海军大将", null);
    return Optional.ofNullable(null);
}

orElseThrow方法,当返回值为Null不存在的时候抛出异常

public static void main(String[] args) {

        Optional<Author> author = getAuthorName();
        Author res = null;
        try {
            res = author.orElseThrow((Supplier<Throwable>) () -> new Exception());
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
        System.out.println(res.getName());
    }

    private static Optional<Author> getAuthorName() {
        Author author = new Author(1L, "青雉", 33, "海军大将", null);
        return Optional.ofNullable(author);
    }

对于抛出的异常,在spring当中可以进行异常的统一处理

七、Stream流的高级性质

基本数据类型优化

        之前使用Stream流的时候使用泛型进行相应的操作,对于涉及的参数和返回值都是引用数据类型,但是在基本数据和包装类之间的关系时,由于自定装箱和拆箱也会造成额外的时间消耗,因此需要进行优化

long time1 = System.currentTimeMillis();
     for (int i = 0; i < 1000000; i++) {
         authors.stream()
                .distinct()
                .map(author -> author.getAge())
                .filter(age -> age > 18)
                .max((o1, o2) -> o2 - o1);
     }

long time2 = System.currentTimeMillis();
System.out.println(time2 - time1); // 47

时间消耗:422

使用mapToInt将流中的数据转换成int类型,不需要装箱和拆箱的操作

long time1 = System.currentTimeMillis();

    for (int i = 0; i < 1000000; i++) {
        authors.stream()
                .distinct()
                .mapToInt(author -> author.getAge())
                .filter(age -> age > 18)
                .max();
    }

long time2 = System.currentTimeMillis();
System.out.println(time2 - time1); 

时间消耗:368

 可以看到提示里流中数据类型

并行流

        当流中的数据很多时候,可以使用并行流提高哦操作效率,通过开启多个线程共同完成这次操作;而使用Stream中提供的并行策略可以减少我们自己实现并发编程的困难和线程安全问题。

使用paraller()可以开启并行流

// 并行流
Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
Optional<Integer> optional = stream.parallel()  // 开启并行流
        .filter(num -> num > 5)
        .map(num -> num + 5)
        .peek(num -> System.out.println(num + ":" + Thread.currentThread().getName()))  // 中间方法打印信息
        .reduce((res, ele) -> res + ele);
optional.ifPresent(res -> System.out.println(res));

 

总结

        记录下学习jdk8新特性的lambda表达式和Stream流相关知识

        视频资源:b站搜索三更草堂,up小哥很棒的,支持!!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值