当前位置: 首页 > news >正文

Java新特性(二) 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(),来从数组中创建一个流;
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 {//idprivate Long id;//书名private String name;//分类private String category;//评分private Integer score;//简介private String intro;
}
@Data
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode//用于后期的去重使用
public class Author {//idprivate 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>() {@Overridepublic boolean test(Author author) {return author.getName().length() > 2;}}).forEach(author -> System.out.println(author.getName()));//Lambdaauthors.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>() {@Overridepublic Integer apply(Author author) {return author.getName().length();}}).filter(new Predicate<Integer>() {@Overridepublic boolean test(Integer len) {return len > 2;}}).forEach(val -> System.out.println(val));//Lambdaauthors.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>() {@Overridepublic Integer apply(Author author) {return author.getAge();}}).map(new Function<Integer, Integer>() {@Overridepublic Integer apply(Integer age) {return age + 10;}}).forEach(age-> System.out.println(age));//Lambdaauthors.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>@Overridepublic Stream<Book> apply(Author author) {return author.getBooks().stream();}}).distinct().forEach(book -> System.out.println(book.getName()));//Lambdaauthors.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>@Overridepublic Stream<Book> apply(Author author) {return author.getBooks().stream();}}).distinct().flatMap(new Function<Book, Stream<String>>() { // Book -> Stream<String>@Overridepublic Stream<String> apply(Book book) {return Arrays.stream(book.getCategory().split(","));}}).distinct().forEach(category-> System.out.println(category));//Lambdaauthors.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> {//idprivate Long id;//姓名private String name;//年龄private Integer age;//国家private String country;//作品private List<Book> books;//负整数、零或正整数,因为此对象小于、等于或大于指定对象。@Overridepublic 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>() {@Overridepublic int compare(Author o1, Author o2) {return o2.getAge() - o1.getAge();}}).forEach(author -> System.out.println(author.getAge()));//Lambdaauthors.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>() {@Overridepublic boolean test(Author author) {return author.getAge() > 80;}});System.out.println(flag);//Lambdaboolean 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>() {@Overridepublic 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>() {@Overridepublic 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>() {@Overridepublic 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;}elseresult = 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>() {@Overridepublic 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>() {@Overridepublic 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());

相关文章:

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

Java8新特性&#xff08;二&#xff09; Stream与Optional详解 一. Stream流 1. Stream概述 1.1 基本概念 Stream&#xff08;java.util.stream&#xff09; 是Java 8中新增的一种抽象流式接口&#xff0c;主要用于配合Lambda表达式提高批量数据的计算和处理效率。Stream不是…...

springboot系列教程(三十一):springboot整合Nacos组件,环境搭建和入门案例详解

一、Nacos基础简介 1、概念简介 Nacos 是构建以“服务”为中心的现代应用架构&#xff0c;如微服务范式、云原生范式等服务基础设施。聚焦于发现、配置和管理微服务。Nacos提供一组简单易用的特性集&#xff0c;帮助开发者快速实现动态服务发现、服务配置、服务元数据及流量管…...

Traefik系列

一、入门Traefik系列——基础简介 官方文档 https://doc.traefik.io/traefik/[1] 简介 Traefik是一个为了让部署微服务更加便捷而诞生的现代HTTP反向代理、负载均衡工具。它支持多种后台 (Docker, Swarm, Kubernetes, Marathon, Mesos, Consul, Etcd, Zookeeper, BoltDB, Re…...

【力扣】3128. 直角三角形 JAVA

一、题目描述 给你一个二维 boolean 矩阵 grid 。 请你返回使用 grid 中的 3 个元素可以构建的 直角三角形 数目&#xff0c;且满足 3 个元素值 都 为 1 。 注意&#xff1a; 如果 grid 中 3 个元素满足&#xff1a;一个元素与另一个元素在 同一行&#xff0c;同时与第三个元素…...

如何全面提升企业安全意识

引言 在当今数字化和信息化的时代&#xff0c;网络安全已成为企业运营不可忽视的核心问题。员工的安全意识直接关系到企业的数据安全和整体网络防护能力。即使企业采用了先进的安全技术&#xff0c;如果员工缺乏足够的安全意识&#xff0c;仍然容易成为攻击者的突破口。本文将…...

全球支持与无界服务:跨越地域的数据采集与分析

在当今企业运营中&#xff0c;IT 监控系统的全球支持和无界服务变得至关重要。随着企业业务的全球化扩展&#xff0c;传统的监控工具往往因地域限制而无法满足全球统一监控的需求。观测云通过其全球部署的数据采集点和多语言支持&#xff0c;确保了无论数据产生于何处&#xff…...

Java面试八股之简述spring boot的目录结构

简述spring boot的目录结构 Spring Boot 项目遵循标准的 Maven 或 Gradle 项目布局&#xff0c;并且有一些约定的目录用于组织不同的项目组件。下面是一个典型的 Spring Boot 项目目录结构&#xff1a; src/main/java&#xff1a;包含所有的 Java 源代码&#xff0c;通常按包组…...

python == 与 is区别

刷到一个面试题 python中 与 is 的区别 根据以往的经验&#xff0c;这个问题应该考察的是运算符根据地址 还是值进行比较的 s1 [a] s2 [a] s3 s1 print(s1 s2) # True 值相等 print(s1 s3) # True 值相等 print(s1 is s2) # False 值相等&#xff0c;引用地址不相…...

STM32学习笔记1---LED,蜂鸣器

目录 GPIO LED 蜂鸣器 RCC外设 GPIO外设 总概 操作STM32的GPIO 代码 LED闪烁 LED流水灯 蜂鸣器&#xff01; 连接方式 GPIO GPIO输出&#xff1a;向外驱动控制 GPIO输入&#xff1a;读取&#xff0c;捕获&#xff08;信息&#xff09;&#xff08;控制&#xff09…...

动手学强化学习 第 15 章 模仿学习 训练代码

基于 https://github.com/boyu-ai/Hands-on-RL/blob/main/%E7%AC%AC15%E7%AB%A0-%E6%A8%A1%E4%BB%BF%E5%AD%A6%E4%B9%A0.ipynb 理论 模仿学习 修改了警告和报错 运行环境 Debian GNU/Linux 12 Python 3.9.19 torch 2.0.1 gym 0.26.2 运行代码 #!/usr/bin/env pythonimpor…...

第一阶段面试问题(前半部分)

1. 进程和线程的概念、区别以及什么时候用线程、什么时候用进程&#xff1f; &#xff08;1&#xff09;线程 线程是CPU任务调度的最小单元、是一个轻量级的进程 &#xff08;2&#xff09;进程 进程是操作系统资源分配的最小单元 进程是一个程序动态执行的过程&#xff0c;包…...

《数学教学通讯》是一本怎样的刊物?投稿难吗?

《数学教学通讯》是一本怎样的刊物&#xff1f;投稿难吗&#xff1f; 《数学教学通讯》是一本具有较高学术价值的教育类刊物。它创刊于 1979 年&#xff0c;由西南大学主管&#xff0c;西南大学数学与统计学院、重庆市数学学会主办&#xff0c;出版周期为旬刊。该刊物在国内外…...

<机器学习> K-means

K-means定义 K-means 是一种广泛使用的聚类算法&#xff0c;旨在将数据集中的点分组为 K 个簇&#xff08;cluster&#xff09;&#xff0c;使得每个簇内的点尽可能相似&#xff0c;而不同簇的点尽可能不同。K-means 算法通过迭代的方式&#xff0c;逐步优化簇的分配和簇的中心…...

我们如何优化 Elasticsearch Serverless 中的刷新成本

作者&#xff1a;来自 Elastic Francisco Fernndez Castao, Henning Andersen 最近&#xff0c;我们推出了 Elastic Cloud Serverless 产品&#xff0c;旨在提供在云中运行搜索工作负载的无缝体验。为了推出该产品&#xff0c;我们重新设计了 Elasticsearch&#xff0c;将存储与…...

MySQL半同步复制

1.MySQL主从复制模式 1.1异步复制 异步复制为 MySQL 默认的复制模式&#xff0c;指主库写 binlog、从库 I/O 线程读 binlog 并写入 relaylog、从库 SQL 线程重放事务这三步之间是异步的。 异步复制的主库不需要关心备库的状态&#xff0c;主库不保证事务被传输到从库&#xf…...

[一本通提高数位动态规划]数字游戏:取模数题解

[一本通提高数位动态规划]数字游戏&#xff1a;取模数题解 1前言2问题3状态的设置4数位dp-part1预处理5数位dp-part2利用状态求解6代码7后记 1前言 本文为数字游戏&#xff1a;取模数的题解 需要读者对数位dp有基础的了解&#xff0c;建议先阅读 论数位dp–胎教级教学 B3883 […...

[Day 39] 區塊鏈與人工智能的聯動應用:理論、技術與實踐

區塊鏈的安全性分析 區塊鏈技術已經成為現代數字經濟的一個重要組成部分&#xff0c;提供了去中心化、透明和不可篡改的數據存儲與交易系統。然而&#xff0c;隨著區塊鏈技術的廣泛應用&#xff0c;其安全性問題也日益受到關注。本篇文章將詳細探討區塊鏈技術的安全性&#xf…...

OpenStack入门体验

一、云计算概述 1.1什么是云计算 云计算(cloud computing)是一种基于网络的超级计算模式,基于用户的不同需求&#xff0c;提供所需的资源&#xff0c;包括计算资源、存储资源、网络资源等。云计算服务运行在若干台高性能物理服务器之上&#xff0c;提供每秒 10万亿次的运算能力…...

预测未来 | MATLAB实现RF随机森林多变量时间序列预测未来-预测新数据

预测未来 | MATLAB实现RF随机森林多变量时间序列预测未来-预测新数据 预测效果 基本介绍 随机森林属于 集成学习 中的 Bagging(Bootstrap AGgregation 的简称) 方法。如果用图来表示他们之间的关系如下: 随机森林是由很多决策树构成的,不同决策树之间没有关联。当我们进行…...

iOS 系统提供的媒体资源选择器(UIImagePickerController)

简介 图片或者视频的选择功能几乎是每个APP必不可少的&#xff0c;UIImagePickerController 是 iOS 系统提供的一个方便的媒体选择器&#xff0c;允许用户从照片库中选择图片或视频&#xff0c;或者使用相机拍摄新照片和视频。 它的页面简单易用&#xff0c;代码稳定可靠&…...

电脑如何扩展硬盘分区?告别空间不足困扰

在数字化时代&#xff0c;电脑硬盘的存储空间显得愈发重要。随着个人文件、应用程序和系统更新的不断累积&#xff0c;原有的硬盘分区可能很快就会被填满。为了解决这个问题&#xff0c;扩展硬盘分区成为了一个非常实用的方法。那么&#xff0c;电脑如何扩展硬盘分区呢&#xf…...

论文阅读:Mammoth: Building math generalist models through hybrid instruction tuning

Mammoth: Building math generalist models through hybrid instruction tuning https://arxiv.org/pdf/2309.05653 MAmmoTH&#xff1a;通过混合指令调优构建数学通才模型 摘要 我们介绍了MAmmoTH&#xff0c;一系列特别为通用数学问题解决而设计的开源大型语言模型&#…...

什么样的双筒式防爆器把煤矿吸引?

什么样的双筒式防爆器把煤矿吸引&#xff1f;要有好的服务和态度&#xff0c;要用心去聆听客户的需求&#xff0c;去解决客户的疑虑&#xff0c;用诚信去赢得客户的信任。 150产品的技术特点 双筒式防爆器采用双罐结构&#xff0c;其水封水位观测直观、能够快速有效排污、操作…...

如何保证冰河AL0 400G 100W 的稳定运行?

要保证冰河 AL0 400G 100w 的稳定运行&#xff0c;可以考虑以下几点&#xff1a; 1. 适宜的工作环境&#xff1a;确保设备放置在通风良好、温度适宜的环境中。良好的散热条件有助于防止设备过热&#xff0c;因为过热可能会导致性能下降或故障。该设备采用纯铝合金外壳&#xf…...

剪画小程序:巴黎奥运会,从画面到声音!

在巴黎奥运会的赛场上&#xff0c;每一个瞬间都伴随着独特的声音。那是观众的欢呼&#xff0c;是运动员冲刺的呐喊&#xff0c;是国歌奏响的激昂旋律。 如今&#xff0c;通过剪画音频提取&#xff0c;我们能够将这些珍贵的声音从精彩的画面中分离出来&#xff0c;单独珍藏。 想…...

【leetcode详解】心算挑战: 一题搞懂涉及奇偶数问题的 “万金油” 思路(思路详解)

前记&#xff1a; 做了几日的leetcode每日一题&#xff0c;几乎全是十分钟结束战斗的【中等】题&#xff0c;今日杀出来个【简单】题&#xff0c;反倒开始难以想出很清楚的解题思路&#xff0c;反复调试修改才将题目逐渐考虑全面&#xff0c;看到了原本思路的漏洞&#xff0c…...

【资料集】数据库设计说明书(Word原件提供)

2 数据库环境说明 3 数据库的命名规则 4 逻辑设计 5 物理设计 5.1 表汇总 5.2 表结构设计 6 数据规划 6.1 表空间设计 6.2 数据文件设计 6.3 表、索引分区设计 6.4 优化方法 7 安全性设计 7.1 防止用户直接操作数据库 7.2 用户帐号加密处理 7.3 角色与权限控制 8 数据库管理与维…...

MySQL 常用查询语句精粹

引言 MySQL 是一种广泛使用的开源关系型数据库管理系统&#xff0c;其强大的查询语言为用户提供了丰富的数据处理能力。掌握 MySQL 的常用查询语句对于数据库管理和数据分析至关重要。本文将介绍一些 MySQL 中的常用查询语句&#xff0c;并提供实际的示例。 基础查询 1. 选择…...

hive的内部表(MANAGED_TABLE)和外部表(EXTERNAL_TABLE)的区别

1.hive的表类型分为外部表和内部表 内部表和外部表的主要区别在于数据的存储方式。 外部表&#xff1a;外部表的存储在hdfs中&#xff0c;是我们指定的文件目录&#xff0c;当我们删除数据或者删除分区的时候不会将元数据删除&#xff0c;数据还会在hdfs目录中&#xff0c;我们…...

【AutoSar网络管理】验证ecu能够从RepeatMessage状态切换到ReadySleep

本专栏将为您提供: Autosar网络管理介绍,包括:状态迁移、状态行为、状态表现、切换条件、时间参数、消息类型等。DUT模拟节点介绍,包括:设计思路、代码展示、编写须知等。测试用例介绍,包括:测试内容、测试步骤、期望结果等。测试脚本介绍,包括:编写思路、代码展示、脚…...

上海网站推广很好/成人职业技能培训学校

做为系统管理员可能会面对的任务:1.自动批量安装操作系统2.完成系统的本地化 (配置现成的发行版或者软件包,以求符合自己的需要,本地安全规定、文 件存放和网络拓扑的需要&#xff0c;这个过程称为“本地化”)3.给系统打补丁且保持系统的更新 4.管理附加的软件包 程…...

坂田网站建设方案/武汉百度开户代理

STM32启动过程--启动文件--分析 一、概述 1、说明 每一款芯片的启动文件都值得去研究&#xff0c;因为它可是你的程序跑的最初一段路&#xff0c;不可以不知道。通过了解启动文件&#xff0c;我们可以体会到处理器的架构、指令集、中断向量安排等内容&#xff0c;是非常值得玩…...

wordpress 启动慢/推广普通话的内容简短

原博文 2020-07-03 16:42 − 利用django实现百度AI 语音识别、合成 RESTful API Python SDK 官方文档&#xff1a;https://ai.baidu.com/ai-doc/SPEECH/tk4o0bm3v 1. 我们要创建百度ai的语音技术应用 2. 查看应用的 appid apike... 相关推荐 2019-09-28 21:13 − Python python…...

php wordpress教学/女教师遭网课入侵视频

http://www.sohu.com/a/311437451_267106 &#xff08;江南水乡&#xff09; 编辑 讨论1中国人所说的“水乡”&#xff0c;一般是指“江南水乡”。中国的江南&#xff0c;大体上是指浙江&#xff0c;上海&#xff0c;安徽&#xff0c;江西和江苏长江以南地区&#xff0c;主要城…...

怎样用阿里云服务器做网站/宁波seo优化排名

https://blog.csdn.net/weixin_33923762/article/details/91556576...

wordpress 登录没反应/宁波seo专员

单链表&#xff08;single-linked list&#xff09;链表结构应用实例分析数据结构算法类方法对象代码实现插入向尾部直接插入节点思路分析算法实现按照顺序插入指定位置思路分析算法实现修改思路分析代码实现删除思路分析代码实现查找思路分析代码实现面试题有效元素的个数代码…...