java8实战读书笔记:数值流、Stream创建与Optional类的使用

简介: java8实战读书笔记:数值流、Stream创建与Optional类的使用

本节将详细介绍Java8中的数值流、流的创建与Optional类的使用。

image.png

不知大家还记不得,在介绍函数式编程接口中为了避免基础数据类型的装箱/拆箱带来的性能损耗,特意为函数式接口引入了基础数据类型的函数式编程接口,例如IntPredicate、LongPredicate、DoublePredicate。同样,流API也考虑到基本数据类型的装箱/拆箱会带来性能损耗,引入了数值流,例如IntStream、LongStream、DoubleStream。


1.1 原始数据特化流


java8中提供了3个原始数据特化流,分别为IntStream、LongStream、DoubleStream。本文将以IntStream进行讲解,其他流类似,只是数据类型分别代表Long或Double。


1.1.1 映射到数据流


首先我们还是从一个示例开始本节的学习:计算菜单中所有菜品的卡路里之和。

1public static void test_reduce_caluli(List<Dish> menu) {
2    int calories = menu.stream()
3                    .map(Dish::getCalories)
4                    .reduce(0, Integer::sum);
5    System.out.println("菜品中的总卡路里:" + calories);
6}

上面包含了一个基本数据类型的装箱/拆箱动作,java8的流API提供了mapToInt方法,直接返回int类型的流

我们先稍微看一下mapToInt的方法声明:

1IntStream mapToInt(ToIntFunction<? super T> mapper)
2

接受一个T->int的函数式编程接口,直接返回IntStream流对象,而且IntStream本身提供了一些常用的聚合函数,例如sum。


使用IntStream来实现计算菜单中所有菜品的卡路里之和,其示例如下:

1public static void test_reduce_caluli_intStream(List<Dish> menu) {
2    int calories = menu.stream()
3            .mapToInt(Dish::getCalories)
4            .sum();
5    System.out.println("菜品中的总卡路里:" + calories);
6
7}


1.1.2 转换回对象流


使用了特化流例如IntStream后,将不能再自动转换为其对应的封装对象流Stream< T >了,我们可以随意从IntStream对象中对应的通用方法的函数声明,例如IntStream#map函数的声明如下:

1IntStream map(IntUnaryOperator mapper);

只能接受int -> int的函数式编程接口,如果想将IntStream转回到Stream< Integer >,该如何处理呢?


IntStream提供了boxed()方法来实现将基础数据类型转换回对应的包装类型的流。


1.1.3 常用函数


Stream中定义的方法,IntStream也可以使用,例如map、flatMap、distinict等,IntStream除这些之外,还提供了常用的聚合函数,例如sum、min、max、average(平均数)。

1OptionalDouble average();
2OptionalInt max();
3OptionalInt min();
4int sum();

有关Optional相关的类将在下文详细介绍。


另外除了上面提到的聚合函数,IntStream还提供了两个与数值范围的方法:

1public static IntStream range(int startInclusive, int endExclusive);
2public static IntStream rangeClosed(int startInclusive, int endExclusive);

rangeClosed与range的区别就是rangeClosed包含结束边界,举一个简单示例如下:

1public static void test_range() {
2    long count = IntStream.range(1,100)
3                        .filter( i -> i % 2 == 0 )
4                         .count();
5    System.out.println("count:" + count);
6
7}

计算【1,100)中包含的偶数个数,将输出49。如果将range(1,100)修改为rangeClosed(1,100),在输出的个数为50。

image.png


2.1 通过值构建流


java 8的Stream提供了两个重载的of函数来显示的构建流,其声明如下:

1public static<T> Stream<T> of(T t)
2public static<T> Stream<T> of(T... values)
3


2.2 通过数组构建流


通过Arrays.stream构建流,其声明如下:


Arrays#stream

1public static <T> Stream<T> stream(T[] array)
2


2.3 通过文件流


可以通过文件流创建流,在java.nio.file.Files类中定义了如下创建流的方法。

1public static Stream<Path> list(Path dir) throws IOException
2public static Stream<Path> walk(Path start, int maxDepth, FileVisitOption... options)
3public static Stream<Path> walk(Path start, FileVisitOption... options)
4public static Stream<Path> find(Path start, int maxDepth,BiPredicate<Path, BasicFileAttributes> matcher,   
5    FileVisitOption... options)
6public static Stream<String> lines(Path path, Charset cs)
7public static Stream<String> lines(Path path) throws IOException
8

下面我们举一个示例:找出一个文件中不同词的个数。

1public static void test_file_stram() {
 2    long uniqueWords = 0;
 3    try(Stream<String> lines = Files.lines(Paths.get("d:/tmp/words.txt"), Charset.defaultCharset())) {  
 4        uniqueWords = lines.flatMap(line -> Arrays.stream(line.split("" )))
 5                .distinct()
 6                .count();
 7
 8        System.out.println("不重复字符个数:" + uniqueWords);
 9    } catch (IOException e) {
10        e.printStackTrace();
11    }
12}


2.4 函数生成流:创建无限流


Stream API提供了两个静态方法从函数生成流:iterate、generate,我们先来看一下其函数声明:

1public static<T> Stream<T> iterate(final T seed, final UnaryOperator<T> f)
2public static<T> Stream<T> generate(Supplier<T> s)
3


2.4.1 iterate


iterate方法的第一个参数类型为T,表示其初始值,第二个参数如下:

b413f61a005fd59e3859651fe606e4a5.jpg

即其函数式声明为为T-T。其示例如下:

1public static void test_iterate() {
2    Stream.iterate(0, a -> a + 2)
3            .limit(10)
4            .forEach(System.out::println);
5}

注意:由于是无限流,故千万记得使用limit截断流,否则会无限循环下去。


2.4.2 generate


其参数为Supplier< T >,其定义如下:

96e3ce9f1fc85b5d211a1be184358416.png

即构造一个T类型的对象,举例如下:

1public static void test_iterate() {
2    Stream.iterate(0, a -> a + 2)
3            .limit(10)
4            .forEach(System.out::println);
5}


2.5 集合对象的stream


这个在前面的示例中用的最多,就不做过多介绍。

image.png

为了更优雅的处理null值,避免空指针错误,java8中引入Optional类。

b7da52362435700d25de7897fc32f918.jpg

接下来对这些方法一一做个介绍。


  • public static< T> Optional< T> empty()
    创建一个Optional对象,其内部持有的对象为null。
  • public static < T > Optional< T > of(T value)
    使用value的值,创建一个Optional对象。
  • public static < T > Optional< T > ofNullable(T value)
    使用v去创建一个Optional对象,如果value为null,则返回empty()。
  • public T get()
    从Optional对象获取内嵌的对象,如果为空,则抛出NoSuchElementException。
  • public boolean isPresent()
    判断Optional对象中包含的值是否存在。
  • public void ifPresent(Consumer consumer)
    如果Optional包裹的对象存在,则消费该对象。Consumer的函数式编程接口:T -> void。
  • public Optional< T > filter(Predicate predicate)
    如果Optional中包裹的对象为空,则返回自身,否则如果包裹的对象满足predicate表达式,则返回自身,否则返回empty()。
  • public< U > Optional< U > map(Function mapper)
    如果Optional对象中包裹的对象为空,则返回empty(),否则运用(T-U),包裹U,当然如果U为空,则返回empty()。
  • public< U > Optional< U > flatMap(Function> mapper)
    如果Option对象中包裹的对象为空,则返回empty(),否则使用对Optional中的包裹的对象value应用Function,最终返回Optional对象。
  • public T orElse(T other)
    返回Optional中包裹的对象,如果其值为空,则返回other。
  • public T orElseGet(Supplier other)
    返回Optional中包裹的对象,如果其值为空,则返回 Supplier函数式编辑接口中创建的值。
  • public < X extends Throwable> T orElseThrow(Supplier< ? extends X> exceptionSupplier) throws X
    返回Optional中包裹的对象,如果其值为空,则抛出自定义一次,由Supplier函数式编程接口返回。


其示例代码如下:


1public static void test_option() {
2Map<String, String> data = new HashMap<>();
3Optional<String> value = Optional.ofNullable(data.get("userName"));
4// 如果存在userName值,则输出
5value.ifPresent(System.out::println);  
6}


本文就介绍到这里了,本文详细介绍了java8中的数值流、Stream的创建以及java8中Optional类的使用。


相关文章
|
5天前
|
Java
java的类详解
在 Java 中,类是面向对象编程的核心概念,用于定义具有相似特性和行为的对象模板。以下是类的关键特性:唯一且遵循命名规则的类名;描述对象状态的私有属性;描述对象行为的方法,包括实例方法和静态方法;用于初始化对象的构造方法;通过封装保护内部属性;通过继承扩展其他类的功能;以及通过多态增强代码灵活性。下面是一个简单的 `Person` 类示例,展示了属性、构造方法、getter 和 setter 方法及行为方法的使用。
|
3天前
|
Java API C++
Java 8 Stream Api 中的 peek 操作
本文介绍了Java中`Stream`的`peek`操作,该操作通过`Consumer&lt;T&gt;`函数消费流中的每个元素,但不改变元素类型。文章详细解释了`Consumer&lt;T&gt;`接口及其使用场景,并通过示例代码展示了`peek`操作的应用。此外,还对比了`peek`与`map`的区别,帮助读者更好地理解这两种操作的不同用途。作者为码农小胖哥,原文发布于稀土掘金。
Java 8 Stream Api 中的 peek 操作
|
3天前
|
Java C# Swift
Java Stream中peek和map不为人知的秘密
本文通过一个Java Stream中的示例,探讨了`peek`方法在流式处理中的应用及其潜在问题。首先介绍了`peek`的基本定义与使用,并通过代码展示了其如何在流中对每个元素进行操作而不返回结果。接着讨论了`peek`作为中间操作的懒执行特性,强调了如果没有终端操作则不会执行的问题。文章指出,在某些情况下使用`peek`可能比`map`更简洁,但也需注意其懒执行带来的影响。
Java Stream中peek和map不为人知的秘密
|
2天前
|
存储 Java
Java的对象和类的相同之处和不同之处
在 Java 中,对象和类是面向对象编程的核心。
|
1天前
|
存储 负载均衡 Java
Jetty技术深度解析及其在Java中的实战应用
【9月更文挑战第3天】Jetty,作为一款开源的、轻量级、高性能的Java Web服务器和Servlet容器,自1995年问世以来,凭借其卓越的性能、灵活的配置和丰富的扩展功能,在Java Web应用开发中占据了举足轻重的地位。本文将详细介绍Jetty的背景、核心功能点以及在Java中的实战应用,帮助开发者更好地理解和利用Jetty构建高效、可靠的Web服务。
11 2
|
8天前
|
Java API 开发者
【Java字节码操控新篇章】JDK 22类文件API预览:解锁Java底层的无限可能!
【9月更文挑战第6天】JDK 22的类文件API为Java开发者们打开了一扇通往Java底层世界的大门。通过这个API,我们可以更加深入地理解Java程序的工作原理,实现更加灵活和强大的功能。虽然目前它还处于预览版阶段,但我们已经可以预见其在未来Java开发中的重要地位。让我们共同期待Java字节码操控新篇章的到来!
|
7天前
|
Java
Java 对象和类
在Java中,**类**(Class)和**对象**(Object)是面向对象编程的基础。类是创建对象的模板,定义了属性和方法;对象是类的实例,通过`new`关键字创建,具有类定义的属性和行为。例如,`Animal`类定义了`name`和`age`属性及`eat()`、`sleep()`方法;通过`new Animal()`创建的`myAnimal`对象即可调用这些方法。面向对象编程通过类和对象模拟现实世界的实体及其关系,实现问题的结构化解决。
|
6天前
|
Java API 开发者
【Java字节码的掌控者】JDK 22类文件API:解锁Java深层次的奥秘,赋能开发者无限可能!
【9月更文挑战第8天】JDK 22类文件API的引入,为Java开发者们打开了一扇通往Java字节码操控新世界的大门。通过这个API,我们可以更加深入地理解Java程序的底层行为,实现更加高效、可靠和创新的Java应用。虽然目前它还处于预览版阶段,但我们已经可以预见其在未来Java开发中的重要地位。让我们共同期待Java字节码操控新篇章的到来,并积极探索类文件API带来的无限可能!
|
7天前
|
Java 开发者
Java中的多线程编程基础与实战
【9月更文挑战第6天】本文将通过深入浅出的方式,带领读者了解并掌握Java中的多线程编程。我们将从基础概念出发,逐步深入到代码实践,最后探讨多线程在实际应用中的优势和注意事项。无论你是初学者还是有一定经验的开发者,这篇文章都能让你对Java多线程有更全面的认识。
14 1
|
4天前
|
Java 程序员
Java编程中的对象和类: 初学者指南
【9月更文挑战第9天】在Java的世界中,对象和类构成了编程的基石。本文将引导你理解这两个概念的本质,并展示如何通过它们来构建你的程序。我们将一起探索类的定义,对象的创建,以及它们如何互动。准备好了吗?让我们开始这段Java的旅程吧!