Java8新特性(二) Stream与Optional详解

Java8新特性(二) Stream与Optional详解

一. Stream流

1. Stream概述

1.1 基本概念

Stream(java.util.stream) 是Java 8中新增的一种抽象流式接口,主要用于配合Lambda表达式提高批量数据的计算和处理效率。Stream不是一种数据结构,它是来自数据源的一种元素序列组织视图,并支持一系列聚合操作,比如过滤、排序、映射、遍历等;其中,数据源可以是一个数组、集合或I/O channel等。

Stream的主要目的在于计算,它以一种声明性方式处理数据集合与数据,是函数式编程模式的重要体现。关于Stream流,可以把它理解为一种管道式的操作集,它允许将一系列计算过程进行链式封装,并像流水线一样对数据源进行处理,最后生成并返回结果集合。

1.2 操作流程

(1)流的创建: 一个数据源(如集合、数组等),用于获取一个流;

(2)中间操作: 一个中间操作链(包含多个中间操作),每个中间操作都会返回一个新的流,用于对数据源的数据进行处理;

(3)终止操作: 一个终止操作,用于执行中间操作链并产生最终结果,注意终止操作后该Stream就不能再使用了;

1.3 基本特性

  • 惰性求值: 流上的中间操作不会立即执行(仅创建视图),只有在遇到终止操作时才会触发计算;
  • 非数据结构: 不会保存数据,也不会存储元素,只保存操作;
  • 不改变源数据: 不会改变源数据,计算结果会保存到新的流中,但对于对象引用来说可以修改其属性值;

2. 流的创建

2.1 从数组创建

  • Stream.of: 调用Stream类的静态方法 of(),通过显示值创建一个流;
  • Arrays.stream: 调用Arrays 的静态方法 stream(),来从数组中创建一个流;
java 复制代码
Integer[] arr = {1,2,3,4,5};
Stream<Integer> stream_arr = Arrays.stream(arr);
// Stream<Integer> stream_of = Stream.of(1, 2, 3, 4, 5);
Stream<Integer> stream_of = Stream.of(arr);

2.2 从单列集合

单列集合如List、Set,Java8 中的 Collection 接口被扩展,提供了获取集合流的方法 Collection.stream()

java 复制代码
List<String> names = Arrays.asList("Alice", "Bob", "Charlie", "David");
Stream<String> stream = names.stream();

2.3 从双列集合

双列集合如Map,一般转换成单列集合后再创建流

java 复制代码
Map<String,Integer> ids = new HashMap<>();
ids.put("小新",19);
ids.put("黑子",17);
ids.put("哆啦",16);
Stream<Map.Entry<String, Integer>> stream = ids.entrySet().stream();

3. 数据准备

java 复制代码
@Data
@AllArgsConstructor
@NoArgsConstructor
@EqualsAndHashCode//用于后期的去重使用
public class Book {
    //id
    private Long id;
    //书名
    private String name;
    //分类
    private String category;
    //评分
    private Integer score;
    //简介
    private String intro;
}
java 复制代码
@Data
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode//用于后期的去重使用
public class Author {
    //id
    private Long id;
    //姓名
    private String name;
    //年龄
    private Integer age;
    //国家
    private String country;
    //作品
    private List<Book> books;
}
java 复制代码
       private static List<Author> getAuthors() {
        //数据初始化
        Author author1 = new Author(1L, "鲁迅", 55, "中国", null);
        Author author2 = new Author(2L, "维克多.雨果", 83, "法国", null);
        Author author3 = new Author(3L, "安东.巴甫洛维奇.契诃夫", 44, "俄国", null);
        Author author4 = new Author(3L, "安东.巴甫洛维奇.契诃夫", 44, "俄国", null);
        //书籍列表
        List<Book> books1 = new ArrayList<>();
        List<Book> books2 = new ArrayList<>();
        List<Book> books3 = new ArrayList<>();
        books1.add(new Book(1L, "朝花夕拾", "散文,回忆", 82, "这部作品收录了鲁迅于1926年创作的10篇回忆性散文"));
        books1.add(new Book(2L, "孔乙己", "短篇小说,讽刺", 99, "深刻揭示了当时社会的黑暗面"));
        books2.add(new Book(3L, "巴黎圣母院", "长篇小说,浪漫主义", 75, "通过离奇和对比手法,描述了15世纪法国的故事"));
        books2.add(new Book(3L, "巴黎圣母院", "长篇小说,浪漫主义", 75, "通过离奇和对比手法,描述了15世纪法国的故事"));
        books2.add(new Book(4L, "悲惨世界", "现实,长篇小说", 86, "这部小说跨越了拿破仑战争及其后的十几年历史"));
        books3.add(new Book(5L, "变色龙", "短篇小说,讽刺", 86, "巧妙地讽刺了沙皇专制制度下封建卫道士的卑躬屈膝和虚伪"));
        books3.add(new Book(6L, "装在套子里的人", "短篇小说", 100, "深刻地揭示了专制制度对个体思想的毒化,以及别里科夫这样的角色如何成为阻碍社会进步的代表"));
        books3.add(new Book(6L, "装在套子里的人", "短篇小说", 100, "深刻地揭示了专制制度对个体思想的毒化,以及别里科夫这样的角色如何成为阻碍社会进步的代表"));

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

        List<Author> authorList = new ArrayList<>(Arrays.asList(author1, author2, author3, author4));
        return authorList;
    }

4. 中间操作

4.1 筛选与切片

方法 描述
filter(Predicate predicate) 可以对流中的元素进行条件过滤,符合过滤条件的才能继续留在流中(Predicate为True)
distinct() 可以去除流中的重复元素(通过元素的 hashCode 和 equals 去重)
limit(long maxSize) 截断流,使流中元素不超过给定数量maxSize(超出的部分将被抛弃),若数量不足则全保留
skip(long n) 跳过流中的前n个元素,返回剩余元素的流;若流中元素不足n个,则返回空流

(1)filter

打印所有姓名长度大于2的作家的姓名。

java 复制代码
    public static void main(String[] args) {
        List<Author> authors = getAuthors();
        //匿名内部类
        authors.stream()
                .filter(new Predicate<Author>() {
                    @Override
                    public boolean test(Author author) {
                        return author.getName().length() > 2;
                    }
                })
                .forEach(author -> System.out.println(author.getName()));
        //Lambda
        authors.stream()
                .filter(author -> author.getName().length() > 2)
                .forEach(author -> System.out.println(author.getName()));

    }

(2)distinct

distinct方法通过元素的 hashCode 和 equals 方法判断去重,只有两元素的equals返回True且同时hashCode也相等才认为是相同元素。注意使用时要重写 hashCode 和 equals,逻辑上来说:

  • equals 方法判断两个对象是相等的,那这两个对象的 hashCode 值也一定要相等;
  • 两个对象有相同的 hashCode 值,他们也不一定是相等的(哈希碰撞);

打印所有作家的姓名,并且要求其中不能有重复元素。

java 复制代码
    public static void main(String[] args) {
        List<Author> authors = getAuthors();
        authors.stream()
                .distinct()
                .forEach(author -> System.out.println(author.getName()));

    }

(3)limit

打印流中前两个作家的姓名。

java 复制代码
    public static void main(String[] args) {
        List<Author> authors = getAuthors();
        authors.stream()
                .limit(2)
                .forEach(author -> System.out.println(author.getName()));

    }

(4)skip

打印流中除第一个作家外的剩余作家的姓名。

java 复制代码
    public static void main(String[] args) {
        List<Author> authors = getAuthors();
        authors.stream()
                .skip(1)
                .forEach(author -> System.out.println(author.getName()));

    }

4.2 映射

方法 描述
map(Function mapper) 接收一个计算函数,该函数会被应用到每个元素上,并将其映射/转换成一个新的元素(一对一,多流独立)
flatMap(Function mapper) 接收一个计算函数,将流中的每个元素都转换成另一个流,并把所有流连接成一个流(一对多,多流混合)

(1)map

  • 提取作家的姓名长度并筛选长度大于2的值。
java 复制代码
public static void main(String[] args) {
        List<Author> authors = getAuthors();
    	//匿名内部类
        authors.stream()
                .map(new Function<Author, Integer>() {
                    @Override
                    public Integer apply(Author author) {
                        return author.getName().length();
                    }
                })
                .filter(new Predicate<Integer>() {
                    @Override
                    public boolean test(Integer len) {
                        return len > 2;
                    }
                })
                .forEach(val -> System.out.println(val));
    	//Lambda
        authors.stream()
                .map(author -> author.getName().length())
                .filter(len -> len > 2)
                .forEach(val -> System.out.println(val));
    }
  • 提取作家的年龄并转换为10年后的值。
java 复制代码
    public static void main(String[] args) {
        List<Author> authors = getAuthors();
        // 匿名内部类
        authors.stream()
                .map(new Function<Author, Integer>() {
                    @Override
                    public Integer apply(Author author) {
                        return author.getAge();
                    }
                })
                .map(new Function<Integer, Integer>() {
                    @Override
                    public Integer apply(Integer age) {
                        return age + 10;
                    }
                })
                .forEach(age-> System.out.println(age));
        //Lambda
        authors.stream()
                .map(author -> author.getAge())
                .map(age -> age + 10)
                .forEach(age-> System.out.println(age));
    }

(2)flatMap

  • 打印所有书籍的名字,要求对重复的元素进行去重。
java 复制代码
    public static void main(String[] args) {
        List<Author> authors = getAuthors();
        // 匿名内部类
        authors.stream()
                .flatMap(new Function<Author, Stream<Book>>() { // Author -> Stream<Book>
                    @Override
                    public Stream<Book> apply(Author author) {
                        return author.getBooks().stream();
                    }
                })
                .distinct()
                .forEach(book -> System.out.println(book.getName()));
        //Lambda
        authors.stream()
                .flatMap(author -> author.getBooks().stream())
                .distinct()
                .forEach(book -> System.out.println(book.getName()));
    }
  • 打印现有书籍的所有独立分类,并对分类进行去重(分类中不能出现 ',' )
java 复制代码
    public static void main(String[] args) {
        List<Author> authors = getAuthors();
        // 匿名内部类
        authors.stream()
                .flatMap(new Function<Author, Stream<Book>>() { // Author -> Stream<Book>
                    @Override
                    public Stream<Book> apply(Author author) {
                        return author.getBooks().stream();
                    }
                })
                .distinct()
                .flatMap(new Function<Book, Stream<String>>() { // Book -> Stream<String>
                    @Override
                    public Stream<String> apply(Book book) {
                        return Arrays.stream(book.getCategory().split(","));
                    }
                })
                .distinct()
                .forEach(category-> System.out.println(category));
        //Lambda
        authors.stream()
                .flatMap(author -> author.getBooks().stream())
                .distinct()
                .flatMap(book -> Arrays.stream(book.getCategory().split(",")))
                .distinct()
                .forEach(category-> System.out.println(category));
    }

4.3 排序

方法 描述
sorted() 对流中元素按默认规则排序(类内必须实现Comparable接口)
sorted(Comparator com) 对流中元素按比较器规则排序

对流中的元素按照年龄进行降序排序,并且要求不能有重复的元素。

(1)实现Comparable接口

java 复制代码
//实现Comparable接口
public class Author implements Comparable<Author> {
    //id
    private Long id;
    //姓名
    private String name;
    //年龄
    private Integer age;
    //国家
    private String country;
    //作品
    private List<Book> books;

    //负整数、零或正整数,因为此对象小于、等于或大于指定对象。
    @Override
    public int compareTo(Author another) {
        return another.getAge() - this.getAge();
    }
}

//降序排序
public static void main(String[] args) {
	List<Author> authors = getAuthors();
	authors.stream()
			.distinct()
			.sorted()
			.forEach(author -> System.out.println(author.getAge()));
}

(2)自定义比较器Comparator

java 复制代码
    public static void main(String[] args) {
        List<Author> authors = getAuthors();
        // 匿名内部类
        authors.stream()
                .distinct()
                .sorted(new Comparator<Author>() {
                    @Override
                    public int compare(Author o1, Author o2) {
                        return o2.getAge() - o1.getAge();
                    }
                })
                .forEach(author -> System.out.println(author.getAge()));
        //Lambda
        authors.stream()
                .distinct()
                .sorted((o1, o2) -> o2.getAge() - o1.getAge())
                .forEach(author -> System.out.println(author.getAge()));
    }

5. 终止操作

5.1 遍历与统计

方法 描述
void forEach(Consumer action) 迭代,遍历流中的每个元素并执行 action
long count() 返回当前流中的元素总数
Optional max(Comparator c) 返回当前流中的最大值Optional
Optional min(Comparator c) 返回当前流中的最小值Optional

(1)count

打印作家所出的所有书籍的数目,注意删除重复元素。

java 复制代码
    public static void main(String[] args) {
        List<Author> authors = getAuthors();
        long count = authors.stream()
                .flatMap(author -> author.getBooks().stream())
                .distinct()
                .count();
        System.out.println(count);
    }

(2)max&min

分别获取这些作家的所出书籍的最高分和最低分并打印。

java 复制代码
    public static void main(String[] args) {
        List<Author> authors = getAuthors();
        Optional<Integer> max = authors.stream()
                .flatMap(author -> author.getBooks().stream())
                .map(book -> book.getScore())
                .max((score1, score2) -> score1 - score2);
        Optional<Integer> min = authors.stream()
                .flatMap(author -> author.getBooks().stream())
                .map(book -> book.getScore())
                .min((score1, score2) -> score1 - score2);
        System.out.println(max.get());
        System.out.println(min.get());
    }

5.2 匹配与查找

方法 描述
boolean anyMatch(Predicate p) 检查流中是否至少包含一个满足匹配条件的元素
boolean allMatch(Predicate p) 检查流中所有元素是否都满足匹配条件
boolean noneMatch(Predicate p) 检查流中所有元素是否都不满足匹配条件
Optional findFirst() 返回当前流中的第一个元素(可能为空)
Optional findAny() 返回当前流中的任意一个元素,无法保证获取的是流中首位元素(可能为空)

(1)anyMatch

判断是否有年龄在29以上的作家。

java 复制代码
    public static void main(String[] args) {
        List<Author> authors = getAuthors();
        // 匿名内部类
        boolean flag = authors.stream()
                .anyMatch(new Predicate<Author>() {
                    @Override
                    public boolean test(Author author) {
                        return author.getAge() > 80;
                    }
                });
        System.out.println(flag);
        //Lambda
        boolean flag_lambda = authors.stream()
                .anyMatch(author -> author.getAge() > 80);
        System.out.println(flag_lambda);
    }

(2)allMatch

判断是否所有的作家都是成年人。

java 复制代码
    public static void main(String[] args) {
        List<Author> authors = getAuthors();
        boolean flag = authors.stream()
                .allMatch(author -> author.getAge() >= 18);
        System.out.println(flag);
    }

(3)noneMatch

判断作家的书籍列表是否都没有超过3本(包含重复)。

java 复制代码
    public static void main(String[] args) {
        List<Author> authors = getAuthors();
        boolean flag = authors.stream()
                .noneMatch(author -> author.getBooks().size() > 3);
        System.out.println(flag);
    }

(4)findFirst

获取一个年龄最大的作家,并输出他的姓名。

java 复制代码
    public static void main(String[] args) {
        List<Author> authors = getAuthors();
        Optional<Author> first = authors.stream()
                .sorted((o1, o2) -> o2.getAge() - o1.getAge())
                .findFirst();
        first.ifPresent(new Consumer<Author>() {
            @Override
            public void accept(Author author) {
                System.out.println(author.getName());
            }
        });
    }

(5)findAny

获取任意一个年龄大于18的作家,如果存在就输出他的名字。

java 复制代码
    public static void main(String[] args) {
        List<Author> authors = getAuthors();
        Optional<Author> optionalAuthor = authors.stream()
                .filter(author -> author.getAge()>18)
                .findAny();
        optionalAuthor.ifPresent(new Consumer<Author>() {
            @Override
            public void accept(Author author) {
                System.out.println(author.getName());
            }
        });
    }

5.3 收集

方法 描述
collect(Collector c) 将流中的元素转换为另一种数据存储形式。Collector 接口中方法的实现决定了如何对流执行收集的操作(如收集到 List、Set、Map);除此之外,Collectors 实用类提供了很多现成的静态方法来创建常见收集器实例。

参考文章: Java Stream collect

本节不考虑自定义Collector接口实现类的方式,只介绍结合Collectors工具类的主要使用方法,主要包括集合转换、分组分区两大功能模块,其中collect、Collector、Collectors的区别和关联如下。

5.3.1 集合转换
方法 描述
Collectors.toList 将流中的元素收集到一个List中
Collectors.toSet 将流中的元素收集到一个Set中(自动去重)
Collectors.toCollection 将流中的元素收集到一个Collection中
Collectors.toMap 将流中的元素映射收集到一个Map中(若包含相同key,则需提供第三参数)
java 复制代码
    public static void main(String[] args) {
        List<Author> authors = getAuthors();
        // 1.获取一个存放所有作者名字的List集合
        List<String> nameList = authors.stream()
                .map(author -> author.getName())
                .collect(Collectors.toList());
        System.out.println(nameList);
        // 2.获取一个所有书名的Set集合(自动去重)
        Set<Book> books = authors.stream()
                .flatMap(author -> author.getBooks().stream())
                .collect(Collectors.toSet());
        System.out.println(books);
        // 3.获取一个Map集合,map的key为作者名,value为List<Book>(若包含相同key,则需提供第三参数否则报错)
        Map<String, List<Book>> map = authors.stream()
                .distinct()
                .collect(Collectors.toMap(author -> author.getName(), author ->
                        author.getBooks()));
        System.out.println(map);
        // 4.获取一个存放所有作者名字的ArrayList集合(指定具体容器类型)
        ArrayList<String> nameArrList = authors.stream()
                .map(author -> author.getName())
                .collect(Collectors.toCollection(ArrayList::new));
        System.out.println(nameArrList);
    }
5.3.2 分组分区
方法 含义说明
Collectors.groupingBy 根据给定的分组函数的值进行分组,输出一个Map对象
Collectors.partitioningBy 根据给定的分区函数的值进行分区,输出一个Map对象,且key始终为布尔值类型

Collectors.partitioningBy()分区收集器的使用方式与Collectors.groupingBy()分组收集器的使用方式基本相同,其区别在于划分维度,其中分组收集器可以有多个组的划分,而分区收集器只会有两个区( true 和 false) 的划分;单纯从使用维度来看,若分组收集器的分组函数返回值为布尔值,则其效果等同于一个分区收集器。因此本节只有着重介绍分组收集器groupingBy的使用。

(1)方法概述

groupingBy()操作需要指定三个关键输入,即分组函数分组容器值收集器

  • 分组函数:一个处理函数,用于基于指定的元素进行处理,返回一个用于分组的值 (即分组结果Map的Key值),对于经过此函数处理后返回值相同的元素,将被分配到同一个组里;
  • 容器函数:一个生成容器的函数,当调用该函数时,生成所需类型的新空Map;
  • 值收集器:对于分组后的数据元素的进一步处理转换逻辑,即value的处理和存储 逻辑,此处还是一个常规的Collector收集器,和collect()方法中传入的收集器完全等同,多用于实现分组数据统计、多级分组等操作;

为了方便使用,在Collectors工具类中提供了三个groupingBy重载实现,其源码如下:

java 复制代码
//单参数:需要传入classifier分组函数,默认使用了HashMap作为容器函数,使用了toList()作为值收集器
public static <T, K> Collector<T, ?, Map<K, List<T>>>
groupingBy(Function<? super T, ? extends K> classifier) {
	return groupingBy(classifier, toList());
}
//双参数:需要传入classifier分组函数和downstream值收集器
public static <T, K, A, D>
Collector<T, ?, Map<K, D>> groupingBy(Function<? super T, ? extends K> classifier,
                                      Collector<? super T, A, D> downstream) {
	return groupingBy(classifier, HashMap::new, downstream);
}
//三参数:可用于指定自定义Map容器,比如LinkedHashMap::new
public static <T, K, D, A, M extends Map<K, D>>
Collector<T, ?, M> groupingBy(Function<? super T, ? extends K> classifier,
                              Supplier<M> mapFactory,
                              Collector<? super T, A, D> downstream) {
	//...
}

(2)实例分析

java 复制代码
public class Test {
    public static void main(String[] args) {
        List<Student> students = getStudents();
        // 1. 按照单维度分组:按照是否成年分组(匿名函数)
        Map<String, List<Student>> collect1 = students.stream()
                .collect(Collectors.groupingBy(new Function<Student, String>() {
                    @Override
                    public String apply(Student student) {
                        if (student.getAge() < 18) {
                            return "未成年";
                        } else {
                            return "已成年";
                        }
                    }
                }));
        System.out.println(collect1);

        // 2. 分组与组内数据的处理:统计男女性别各有多少人(lambda)
        Map<String, Long> collect2 = students.stream()
                .collect(Collectors.groupingBy(student -> {
                    if (student.getGender() == 1) {
                        return "男";
                    } else {
                        return "女";
                    }
                }, Collectors.counting()));
        System.out.println(collect2);

        // 3. 多级分组:先按国家分组,然后按照成绩是否及格分组
        Map<String, Map<String, List<Student>>> collect3 = students.stream()
                .collect(Collectors.groupingBy(student -> student.getCountry(),
                        Collectors.groupingBy(student -> {
                            if (student.getScore() >= 60) {
                                return "及格";
                            } else {
                                return "不及格";
                            }
                        }))
                );
        System.out.println(collect3);

    }

    private static List<Student> getStudents() {
        //数据初始化
        Student student1 = new Student(1,"张三",1,"祖安",17,88);
        Student student2 = new Student(2,"王雪",0,"祖安",23,92);
        Student student3 = new Student(3,"李四",1,"德玛西亚",31,74);
        Student student4 = new Student(4,"赵一",1,"祖安",25,58);
        Student student5 = new Student(5,"安琪拉",0,"德玛西亚",19,66);
        Student student6 = new Student(6,"大桥",0,"德玛西亚",13,86);

        List<Student> students = new ArrayList<>();
        students.add(student1);
        students.add(student2);
        students.add(student3);
        students.add(student4);
        students.add(student5);
        students.add(student6);

        return students;
    }
}

5.4 归并

方法 描述
reduce(T identity, BinaryOperator accumulator) 可以将流中元素反复结合起来,得到一个值,返回T。其中,identity是初始值,accumulator是归并计算方法。
reduce(BinaryOperator accumulator) 可以将流中元素反复结合起来,得到一个值,返回 Optional<T>。其中,accumulator是归并计算方法,初始值默认为流中第一个元素

(1)双参方法源码

java 复制代码
T result = identity;
for (T element : this stream)
	result = accumulator.apply(result, element)
return result;

(2)单参方法源码

java 复制代码
boolean foundAny = false;
T result = null;
for (T element : this stream) {
	if (!foundAny) {
		foundAny = true;
		result = element;
	}
	else
		result = accumulator.apply(result, element);
}
return foundAny ? Optional.of(result) : Optional.empty();

(3)实例分析

java 复制代码
    public static void main(String[] args) {
        List<Author> authors = getAuthors();
        // 使用reduce求所有作者年龄的和(匿名函数)
        Integer sum = authors.stream()
                .distinct()
                .map(author -> author.getAge())
                .reduce(0, new BinaryOperator<Integer>() {
                    @Override
                    public Integer apply(Integer result, Integer element) {
                        return result + element;
                    }
                });
        System.out.println(sum);
        // 使用reduce求所有作者中年龄的最大值
        Integer max = authors.stream()
                .map(author -> author.getAge())
                .reduce(Integer.MIN_VALUE, (result, element) -> result < element ? element : result);
        System.out.println(max);
        // 使用reduce求所有作者中年龄的最小值(单参方法)
        Optional<Integer> minOptional = authors.stream()
                .map(author -> author.getAge())
                .reduce((result, element) -> result > element ? element : result);
        minOptional.ifPresent(age-> System.out.println(age));
    }

6. 并行流

当流中有大量元素时,我们可以使用并行流去提高操作的效率,其本质就是把任务拆分并分配给多个线程去完成,核心是通过Fork/Join框架实现。获取并行流的方式有两种:

  • parallel(): 把串行流转换成并行流;
  • parallelStream(): 从数据源中直接获取并行流对象;
java 复制代码
    public static void main(String[] args) {
        Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
        /**
         *  peek(Consumer c)
         *      - 中间操作: 对流中的每个元素执行操作,而不影响流的整体后续处理流程
         *      - 特点: 不改变流的原始内容,也不决定流的最终输出结果
         *      - 用途: action动作会在流的每个元素上执行一次,但具体执行多少次取决于下游的终端操作
         *      - 注意:
         *          - 对于并行流,peek()操作的执行顺序没有保证,而且可能会多次执行
         *          - peek()通常用于读取或打印流元素,而不是修改它们。虽然理论上可以尝试在peek()中修改元素,但由于流的惰性求值和可能的不可变性,这样的修改可能不会反映到源集合或后续流操作中。
         */
        //1. parallel转化
        Integer sum = stream.parallel()
                .peek(new Consumer<Integer>() {
                    @Override
                    public void accept(Integer num) {
                        System.out.println(num+Thread.currentThread().getName());
                    }
                })
                .filter(num -> num > 5)
                .reduce((result, ele) -> result + ele)
                .get();
        System.out.println(sum);
        //2. parallelStream直接获取
        List<Author> authors = getAuthors();
        authors.parallelStream()
                .map(author -> author.getAge())
                .map(age -> age + 10)
                .filter(age->age>18)
                .map(age->age+2)
                .forEach(System.out::println);
    }

二. Optional

1.Optional概述

我们在编写代码的时候出现最多的就是空指针异常(NPE) ,所以在很多情况下需要人为做各种非空的判断,但这种方式会让代码变得臃肿不堪、难以理解;为了解决这个问题,JDK8中引入了Optional容器类 ,用于封装结果或数据,并提供一系列空值检测和处理方法,这使得我们可以通过更优雅的方式来避免空指针异常情况。

注意: Mybatis 3.5版本已支持Optional,当dao方法的返回值类型定义成Optional类型时,MyBastis会自动将数据封装成Optional对象返回。

2.Optional使用

2.1 创建对象

方法 描述
Optional.empty 返回一个空的 Optional 实例,其封装数据为null
Optional.of 将指定值用 Optional 封装之后返回,如果该值为 null,则会抛出一个 NullPointerException 异常
Optional.ofNullable 将指定值用 Optional 封装之后返回,如果该值为 null,则返回一个空的 Optional 对象(无异常)
java 复制代码
Author author = getAuthor();
// 1.一般使用Optional的静态方法ofNullable来把数据封装成一个Optional对象
Optional<Author> authorOptional1 = Optional.ofNullable(author);
// 2.当确定一个对象不是空的则可以使用Optional的静态方法of来把数据封装成Optional对象
Optional<Author> authorOptional2 = Optional.of(author);
// 3.通过静态工厂方法 Optional.empty(),创建一个空的 Optional 对象
Optional<Author> authorOptional3 = Optional.empty();

2.2 常用方法

方法 描述
get 如果该值存在,将该值返回,否则抛出一个 NoSuchElementException 异常(不安全)
ifPresent(Comsumer c) 如果值存在,就执行使用该值的消费方法Comsumer,否则什么也不做
isPresent() 如果值存在就返回 true,否则返回 false
orElse(T other) 如果有值则将其返回,否则返回一个默认值other
orElseGet(Supplier other) 如果有值则将其返回,否则返回一个由指定的 Supplier 接口生成的值
orElseThrow(Supplier exceptionSupplier) 如果有值则将其返回,否则抛出一个由指定的 Supplier 接口生成的异常
filter 如果值存在并且满足提供的谓词,就返回包含该值的 Optional 对象;否则返回一个空的 Optional 对象
map 如果值存在,就对该值执行提供的mapping 函数调用
java 复制代码
Optional<Author> authorOptional = Optional.ofNullable(getAuthor());
// 安全消费值
authorOptional.ifPresent(author ->System.out.println(author.getName()));
// 安全获取值
Author author1 = authorOptional.orElseGet(() -> new Author());
相关推荐
qmx_0713 分钟前
HTB-Jerry(tomcat war文件、msfvenom)
java·web安全·网络安全·tomcat
为风而战22 分钟前
IIS+Ngnix+Tomcat 部署网站 用IIS实现反向代理
java·tomcat
编程零零七1 小时前
Python数据分析工具(三):pymssql的用法
开发语言·前端·数据库·python·oracle·数据分析·pymssql
技术无疆2 小时前
快速开发与维护:探索 AndroidAnnotations
android·java·android studio·android-studio·androidx·代码注入
2401_858286112 小时前
52.【C语言】 字符函数和字符串函数(strcat函数)
c语言·开发语言
铁松溜达py2 小时前
编译器/工具链环境:GCC vs LLVM/Clang,MSVCRT vs UCRT
开发语言·网络
everyStudy2 小时前
JavaScript如何判断输入的是空格
开发语言·javascript·ecmascript
C-SDN花园GGbond4 小时前
【探索数据结构与算法】插入排序:原理、实现与分析(图文详解)
c语言·开发语言·数据结构·排序算法
迷迭所归处5 小时前
C++ —— 关于vector
开发语言·c++·算法
架构文摘JGWZ5 小时前
Java 23 的12 个新特性!!
java·开发语言·学习