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

news2024/9/20 20:49:08

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(),来从数组中创建一个流;
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()

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

2.3 从双列集合

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

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. 数据准备

@Data
@AllArgsConstructor
@NoArgsConstructor
@EqualsAndHashCode//用于后期的去重使用
public class Book {
    //id
    private Long id;
    //书名
    private String name;
    //分类
    private String category;
    //评分
    private Integer score;
    //简介
    private String intro;
}
@Data
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode//用于后期的去重使用
public class Author {
    //id
    private Long id;
    //姓名
    private String name;
    //年龄
    private Integer age;
    //国家
    private String country;
    //作品
    private List<Book> books;
}
       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的作家的姓名。

    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 值,他们也不一定是相等的(哈希碰撞);

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

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

    }

(3)limit

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

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

    }

(4)skip

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

    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的值。
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年后的值。
    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

  • 打印所有书籍的名字,要求对重复的元素进行去重。
    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()));
    }
  • 打印现有书籍的所有独立分类,并对分类进行去重(分类中不能出现 ‘,’ )
    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接口

//实现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

    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

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

    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

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

    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以上的作家。

    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

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

    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本(包含重复)。

    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

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

    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的作家,如果存在就输出他的名字。

    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,则需提供第三参数)
    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重载实现,其源码如下:

//单参数:需要传入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)实例分析

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)双参方法源码

T result = identity;
for (T element : this stream)
	result = accumulator.apply(result, element)
return result;

(2)单参方法源码

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)实例分析

    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(): 从数据源中直接获取并行流对象;

在这里插入图片描述

    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 对象(无异常)
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 函数调用
Optional<Author> authorOptional = Optional.ofNullable(getAuthor());
// 安全消费值
authorOptional.ifPresent(author ->System.out.println(author.getName()));
// 安全获取值
Author author1 = authorOptional.orElseGet(() -> new Author());

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1974589.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

远程控制电脑的正确姿势,3大神器助你秒变技术达人!

现在的生活节奏快得跟打鼓似的&#xff0c;不管是在家工作、帮朋友修电脑&#xff0c;还是想控制家里的播放器放个电影&#xff0c;远程控制电脑这事儿越来越重要了。有没有遇到过想用电脑却够不着的尴尬&#xff1f;别急&#xff0c;今天咱们就来看看怎么搞定远程控制电脑&…

快瞳宠物AI识别赋能养宠智能设备,让品牌大有可为

随着国内养宠市场的不断完善与成熟&#xff0c;许多家庭养宠理念从“健康养宠”向“育儿式养宠”的升级&#xff0c;国内宠物行业向高质量发展阶段迈进&#xff0c;宠物经济增长迅猛。报告显示&#xff0c;2024年宠物智能设备货架电商年销售额达2.5亿&#xff0c;增速近30%。内…

记录一次学习过程(msf、cs的使用、横向渗透等等)

目录 用python搭建一个简单的web服务器 代码解释 MSF msfvenom 功能 用途 查看payloads列表 msfconsole 功能 用途 msfvenom和msfconsole配合使用 来个例子 msf会话中用到的一些命令 在windows中net user用法 列出所有用户账户 显示单个用户账户信息 创建用户账…

x-cmd mod | x jq - 轻量级的 JSON 处理工具

目录 简介使用语法参数子命令x jq openx jq repl 简介 jq 是一个轻量级的 JSON 处理工具&#xff0c;是由 Stephen Dolan 于 2012 年开发。 使用语法 x jq [SUB_COMMAND] <#n>参数 参数描述#n继承 jq 子命令或参数选项 子命令 名称描述x jq open用浏览器打开 jqplay…

Axure入门及快速上手的法宝元件库:解锁高效原型设计之旅

在当今快速迭代的数字产品时代&#xff0c;原型设计成为了连接产品创意与实现之间不可或缺的桥梁。Axure RP&#xff0c;作为一款强大的交互原型设计工具&#xff0c;凭借其易用性、灵活性和丰富的功能&#xff0c;成为了设计师和产品经理的首选。它不仅能够帮助用户快速创建高…

Vue3 + cropper 实现裁剪头像的功能(裁剪效果可实时预览、预览图可下载、预览图可上传到SpringBoot后端、附完整的示例代码和源代码)

文章目录 0. 前言1. 裁剪效果&#xff08;可实时预览&#xff09;2. 安装 cropper3. 引入 Vue Cropper3.1 局部引入&#xff08;推荐使用&#xff09;3.2 全局引入 4. 在代码中使用4.1 template部分4.2 script部分 5. 注意事项6. SpringBoot 后端接收图片6.1 UserController.ja…

无线蓝牙耳机哪个品牌好?甄选四款专业蓝牙耳机品牌推荐

随着市场上品牌和型号众多&#xff0c;挑选出最适合自己的蓝牙耳机却变成了一项不小的挑战&#xff0c;不同的用户有着不同的需求——有的人追求音质、有的人注重续航、有的人在意舒适度&#xff0c;还有的人看重的是设计与功能性&#xff0c;那么无线蓝牙耳机哪个品牌好&#…

springboot物流寄查系统-计算机毕业设计源码95192

目 录 1 绪论 1.1 研究背景 1.2选题背景 1.3论文结构与章节安排 2 springboot物流寄查系统系统分析 2.1 可行性分析 2.1.1 技术可行性分析 2.1.2 经济可行性分析 2.1.3 法律可行性分析 2.2 系统功能分析 2.2.1 功能性分析 2.2.2 非功能性分析 2.3 系统用例分析 2…

Maven实战(三)- Maven仓库

Maven实战&#xff08;三&#xff09;- Maven仓库 文章目录 Maven实战&#xff08;三&#xff09;- Maven仓库1.Maven仓库概念2.仓库布局3.仓库分类3.1.本地仓库3.2.远程仓库3.3.中央仓库3.4.私服 4.远程仓库的配置5.远程仓库认证6.部署构件至远程仓库7.从仓库解析依赖8.镜像 1…

牛客JS题(十九)继承

注释很详细&#xff0c;直接上代码 涉及知识点&#xff1a; 构造函数实现类ES6类的写法原型链的应用 题干&#xff1a; 我的答案 <!DOCTYPE html> <html><head><meta charset"utf-8" /></head><body><script type"text…

Python数值计算(17)——Hermite插值

这次介绍一下使用差商表构造Hermite多项式的方法。 前面介绍到了两种很经典的插值多项式&#xff0c;即Lagrange和Newton插值多项式&#xff0c;并在前一篇中阐述了如何通过Lagrange插值方式构造Hermite多项式&#xff0c;这次通过牛顿差商法构造Hermite多项式。 1. 数学原理 …

学生党蓝牙耳机哪个牌子好用性价比高?四大顶尖精品蓝牙耳机揭秘

近年来&#xff0c;市面上的蓝牙耳机品牌如雨后春笋般涌现&#xff0c;各大厂商纷纷跨界合作&#xff0c;推出外观时尚、设计新颖的产品&#xff0c;以吸引各位学生党的目光。然而&#xff0c;在这繁华背后&#xff0c;不少产品却忽视了音质、舒适度及适用性等核心要素&#xf…

2024年【广东省安全员A证第四批(主要负责人)】新版试题及广东省安全员A证第四批(主要负责人)考试技巧

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 广东省安全员A证第四批&#xff08;主要负责人&#xff09;新版试题参考答案及广东省安全员A证第四批&#xff08;主要负责人&#xff09;考试试题解析是安全生产模拟考试一点通题库老师及广东省安全员A证第四批&…

1. 什么是操作系统

文章目录 1.1 从功能上来看操作系统1.2 硬件资源 1.1 从功能上来看操作系统 对用户来说&#xff0c;操作系统是一个控制软件&#xff0c;可以用来管理应用程序&#xff0c;它可以限制不同的程序来占用的资源。对内部的软件来说&#xff0c;操作系统是一个管理外设和分配资源的…

LLM 大模型文档语义分块、微调数据集生成

1、LLM 大模型文档语义分块 参考: https://blog.csdn.net/m0_59596990/article/details/140280541 根据上下句的语义相关性,相关就组合成一个分块,不相关就当场两个快 语义模型用的bert-base-chinese: https://huggingface.co/google-bert/bert-base-chinese 代码: 对…

武汉流星汇聚:亚马逊赋能中国卖家全球化战略深化,业绩斐然赢未来

在全球电商的浩瀚星空中&#xff0c;亚马逊无疑是最耀眼的星辰之一&#xff0c;其强大的平台影响力和全球覆盖能力&#xff0c;为无数商家特别是中国卖家提供了前所未有的发展机遇。近年来&#xff0c;中国卖家在亚马逊平台上的表现尤为亮眼&#xff0c;不仅销量持续攀升&#…

Python面试宝典第26题:最长公共子序列

题目 一个字符串的子序列是指这样一个新的字符串&#xff1a;它是由原字符串在不改变字符的相对顺序的情况下删除某些字符&#xff08;也可以不删除任何字符&#xff09;后组成的新字符串。比如&#xff1a;"ace" 是 "abcde" 的子序列&#xff0c;但 "…

基于redis实现优惠劵秒杀下单功能(结合黑马视频总结)

基础业务逻辑 初步实现 Override public Result seckillVoucher(Long voucherId) {// 1.查询优惠券SeckillVoucher voucher seckillVoucherService.getById(voucherId);// 2.判断秒杀是否开始if (voucher.getBeginTime().isAfter(LocalDateTime.now())) {// 尚未开始return R…

YOLOv8改进 | 注意力机制 | 反向残差注意力机制【内含创新技巧思维】

秋招面试专栏推荐 &#xff1a;深度学习算法工程师面试问题总结【百面算法工程师】——点击即可跳转 &#x1f4a1;&#x1f4a1;&#x1f4a1;本专栏所有程序均经过测试&#xff0c;可成功执行&#x1f4a1;&#x1f4a1;&#x1f4a1; 专栏目录 &#xff1a;《YOLOv8改进有效…

【Docker系列】Docker 镜像管理:删除无标签镜像的技巧

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…