函数编程:强大的 Stream API

函数编程:强大的 Stream API

在这里插入图片描述

每博一文案

只要有人的地方,世界就不会是冰冷的,我们可以平凡,但绝对不可以平庸。
                                          —————— 《平凡的世界》

人活着,就得随时准备经受磨难。他已经看过一些书,知道不论是普通人还是了不起的人,
都要在自己的一生中经历许多磨难。磨难使人坚强。
—————— 《平凡的世界》

hellip... 人哪,活着是这么的苦,一旦你从幸福的彼岸被抛到苦难的此岸,你真
是处处走投无路,而现在你才知道,在天堂与地狱之间原来也只有一步之遥。
——————《平凡的世界》

@

1. Stream 的概述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

; 另外一个则为是我们这个主题了:Stream API 了。

  • Stream API 是在 java.util.stream 包下的,Stream 是把真正的函数式编程 风格引入到 Java 中,这时目前为止对 java 类库最好的补充了,因为 Stream API 可以极大的提供 Java 程序员的生产力,让程序员写出更高效率,干净,简洁的代码。
  • Stream 是 java8 中处理集合的关键抽象概念,它可以指定你希望对集合进行的操作,可以执行非常复杂的查找,过滤和映射数据等操作,使用 Stream API 对集合数据进行操作,就类似于使用 SQL 执行的数据库查询 也可以使用 Stream API 的来并行执行操作。简而言之,Stream API 提供了一种高效且易于使用的处理数据的方式。

1.1 为什么要使用 Stream API

在实际开发种,项目中多数据源都是来自于 MySQ,Oracle 等数据库的,但现在数据源可以更多了,有 MongDB,Radis 等,而这些 NoSQL 的数据就需要 Java 层面去处理。

1.2 什么是 Stream

是数据渠道,用于操作数据源(集合,数组等)所生成的元素序列,“集合讲的是数据,Stream 讲的是计算”

注意:

  • Stream 自己不会存储元素。
  • Stream 不会改变源对象,相反,他们会返回一个持有结果的新 Stream 。这一点和 String 不可变的特点类似。
  • Stream 操作是延迟执行的。这意味着它们会等到需要结果的时候才执行。

1.3 Stream 的操作三个步骤

  1. 创建 Stream

一个数据源(如:集合,数组),获取一个流

2. 中间操作

一个中间操作链,对数据源的数据进行处理。

3. 终止操作(终端操作)

一旦执行终止操作,就执行中间操作链,并产生结果,之后,不会再被使用(也不可再使用)。

在这里插入图片描述

1.4 Stream 与 Colliection 的区别

Stream 和以前的 Collection 操作不同, Stream 操作还有两个基础的特征:

  • Pipelining: 中间操作都会返回流对象本身。 这样多个操作可以串联成一个管道。 这样做可以对操作进行优化, 比如延迟执行.

  • 内部迭代: 以前对集合遍历都是通过 Iterator 或者 For-Each 的方式, 显式的在集合外部进行迭代, 这叫做外部迭代。 Stream 提供了内部迭代的方式, 通过访问者模式 (Visitor) 实现。

  • 当使用一个流的时候,通常包括三个基本步骤:获取一个数据源(source)→ 数据转换→执行操作获取想要的结果,每次转换原有 Stream 对象不改变,返回一个新的 Stream 对象(可以有多次转换),这就允许对其操作可以像链条一样排列,变成一个管道。
  • StreamCollection 集合的主要区别:Collection 是一种静态的内存数据结构,而 Stream 是有关计算的。前者是主要面向内存,存储在内存中后者是主要是面向 CPU 。通过 CPU 实现计算的。

2. 创建 Stream 的四种方式

因为 Stream 是一个接口,所以我们无法通过 new 的方式创建该对象。

2.1 创建 Stream 方式一:通过集合

Java8 中的 Collection 接口被扩展,提供了两个获取流 的方法:

  • default Stream stream() : 返回一个顺序流

  • default Stream parallelStream() : 返回一个并行流

package blogs.blog13;

import day33.java.Employee;
import day33.java.EmployeeData;

import java.util.List;
import java.util.stream.Stream;

public class StreamAPITest {
public static void main(String[] args) {
List<Employee> employeeList = EmployeeData.getEmployees();
//default Stream<E> stream(): 返回一个顺序流
Stream<Employee> stream = employeeList.stream();
System.out.println(stream);

    <span class="hljs-comment">// default Stream&lt;E&gt; parallelStream : 返回一个并行流</span>
    Stream&lt;Employee&gt; employeeStream = employeeList.parallelStream();
    System.out.println(employeeStream);
}

}

在这里插入图片描述

2.2 创建 Stream 方式二:通过数组

Java8 中的 Arrays 的静态方法 stream() 可以获取数组流:

  • static Stream stream(T[] array): 返回一个流

重载形式,能够处理对应基本类型的数组:

  • public static IntStream stream(int[] array)

  • public static LongStream stream(long[] array)

  • public static DoubleStream stream(double[] array)

import java.util.Arrays;
import java.util.List;
import java.util.stream.IntStream;
import java.util.stream.Stream;

public class StreamAPITest {
public static void main(String[] args) {
int[] arr = new int[]{1,2,3,4,5,6};

    <span class="hljs-comment">// 调用 Arrays 类中的 static&lt;T&gt; stream(T[] array)返回一个对象</span>
    <span class="hljs-type">IntStream</span> <span class="hljs-variable">stream</span> <span class="hljs-operator">=</span> Arrays.stream(arr);
    System.out.println(stream);

}

}

在这里插入图片描述

2.3 创建 Stream 的方式三: 通过 Stream 的 of()

可以调用 Stream 类静态方法 of(), 通过显示值创建一个流。它可以接收任意数量的参数。

  • public static Stream of(T... values) : 返回一个流
import java.util.stream.Stream;

public class StreamAPITest {
public static void main(String[] args) {
Stream<Integer> stream = Stream.of(1,2,3,4,5,6);
System.out.println(stream);
}
}

在这里插入图片描述

2.4 创建 Stream 方式四: 创建无限流

可以使用静态方法 Stream.iterate()Stream.generate(), 创建无限流。

  • 迭代

public static Stream iterate(final T seed, final UnaryOperator f)

  • 生成

public static Stream generate(Supplier s)

import java.util.stream.Stream;

public class StreamAPITest {
public static void main(String[] args) {
// 迭代:
//public static<T> Stream<T> inerate(final T seed,final UnaryOperator<T> f)
// 遍历前 10 个偶数
Stream.iterate(0,t->t+2).forEach(System.out::println);

    <span class="hljs-comment">// 生成:</span>
    <span class="hljs-comment">// public static&lt;T&gt; Stream&lt;T&gt; generate(Supplier&lt;T&gt; s)</span>
    Stream.generate(Math::random).limit(<span class="hljs-number">10</span>).forEach(System.out::println);

}

}

在这里插入图片描述

3. Stream 的中间操作

多个中间操作可以连接起来形成一个 流水线 ,除非流水线上触发终止操作,否则中间操作不会执行任何的处理!而终止操作时一次性全处理。 这样的 称为 惰性求值

3.1 筛选与切片

如下是关于 Stream 中间操作筛选与切片的一些常用的方法

  • Stream filter(Predicate<? super T> predicate); // 接收 Lambda 表达式,从流中排除某些元素
package blogs.blog13;

import day33.java.Employee;
import day33.java.EmployeeData;

import java.util.List;
import java.util.stream.Stream;

public class StreamAPITest02 {
public static void main(String[] args) {
List<Employee> list = EmployeeData.getEmployees();
// 创建一个 Stream 对象
Stream<Employee> stream = list.stream();
// 使用 filter 进行一个筛选
// boolean test(T t)
// 筛选出:工资大于 7000 的 Employee 对象
stream.filter(e->e.getSalary() > 7000).forEach(System.out::println);

}

}

在这里插入图片描述

注意: stream 和集合中的迭代器是一样的,不可多次不同结构的使用。 再次使用时需要新建一个 stream 对象,才能使用。简单的来说:就是 stream 对象一次只能对应一次操作,再进行一个新的操作时,必须新建一个 stream 对象才行。不然报java.lang.IllegalStateException异常。

在这里插入图片描述

  • Stream distinct(); // 筛选,通过流所生成元素的 hashCode() 和 equals() 去除重复元素

注意: 使用该方法,因为涉及到筛选,需要对元素数据进行一个比较判断,所以和集合同理:我们必须重写其元素的 hashCode()和 equals() 方法,不然报异常;

import day33.java.Employee;
import day33.java.EmployeeData;

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Stream;

public class StreamAPITest02 {
public static void main(String[] args) {
List<Employee> list = new ArrayList<Employee>();
// distinct()筛选,通过流所生成元素的 hashCode() 和 equals() 去除重复元素
// 所以对应存储的 Employee 对象需要重写 hashCode()和 equals() 方法
list.add(new Employee(1010,"刘强东",40,8000));
list.add(new Employee(1010,"刘强东",40,8000));
list.add(new Employee(1010,"刘强东",40,8000));
list.add(new Employee(1010,"刘强东",20,8000));

    list.stream().distinct().forEach(System.out::println);


}

}

在这里插入图片描述

  • Stream limit(long maxSize); // 截断流,使其元素不超过给定数量
import day33.java.Employee;
import day33.java.EmployeeData;

import java.util.List;
import java.util.stream.Stream;

public class StreamAPITest02 {
public static void main(String[] args) {
List<Employee> list = EmployeeData.getEmployees();
// 创建一个 Stream 对象
Stream<Employee> stream = list.stream();

    <span class="hljs-comment">// limit(n) 截断流: 筛选出 list 集合中存储的前3 个信息</span>
    stream.limit(<span class="hljs-number">3</span>).forEach(System.out::println);

}

}

在这里插入图片描述

  • Stream skip(long n); // 跳过元素,返回一个扔掉了前 n 个元素的流,若流中元素不足 n 个,则返回一个空流,与 limit(n) 互补。

import day33.java.Employee;
import day33.java.EmployeeData;

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Stream;

public class StreamAPITest02 {
public static void main(String[] args) {
List<Employee> list = EmployeeData.getEmployees();
// skip(n) 跳过元素,返回一个扔掉了前 n 个元素的流,若流中元素不足 n 个,则返回一个 空
// stream 不可二次使用,需要新建
Stream<Employee> skip = list.stream().skip(3);
skip.forEach(System.out::println); // 该操作是终止操作,并运用了方法引用

}

}

在这里插入图片描述

3.2 映射

  • Stream map(Function<? super T,? extends R> mapper) 接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素。 注意: 返回的是一个新的对象,不会修改原本的数据信息的。

在这里插入图片描述


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

public class StreamAPITest02 {
public static void main(String[] args) {
List<String> list = Arrays.asList("aa","bb","cc");

    <span class="hljs-comment">// 创建一个 Stream 对象</span>
    Stream&lt;String&gt; stream = list.stream();
    Stream&lt;String&gt; stringStream = stream.map(s -&gt; s.toUpperCase());  <span class="hljs-comment">// toUpperCase() 将字母转换为大写的</span>
    stringStream.forEach(System.out::println);  <span class="hljs-comment">// Stream  终止操作</span>
}

}

在这里插入图片描述


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

public class StreamAPITest02 {

<span class="hljs-comment">// 将字符串的多个字符构造的从集合转换为单个字符串并存储到 List 集合中</span>
<span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> Stream&lt;Character&gt; <span class="hljs-title function_">fromStringToStream</span><span class="hljs-params">(String str)</span> {
    ArrayList&lt;Character&gt; list = <span class="hljs-keyword">new</span> <span class="hljs-title class_">ArrayList</span>&lt;&gt;();

    <span class="hljs-keyword">for</span> (Character character : str.toCharArray()) { <span class="hljs-comment">// toCharArray()将一个字符串拆分为单个字符</span>
        list.add(character);
    }

    <span class="hljs-keyword">return</span> list.stream();
}

<span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">main</span><span class="hljs-params">(String[] args)</span> {
    <span class="hljs-comment">// flatMap(Function f) 接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有的</span>
    <span class="hljs-comment">// 数据组成一个数据</span>
    List&lt;String&gt; list = Arrays.asList(<span class="hljs-string">"aa"</span>,<span class="hljs-string">"AA"</span>,<span class="hljs-string">"bb"</span>);

    Stream&lt;String&gt; stream = list.stream();
    Stream&lt;Character&gt; characterStream = stream.flatMap(StreamAPITest02::fromStringToStream); <span class="hljs-comment">// 方法引用</span>
    characterStream.forEach(System.out::println);


}

}

在这里插入图片描述

举例: 练习:获取员工姓名长度大于 3 的员工的姓名:

import day33.java.Employee;
import day33.java.EmployeeData;

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

public class StreamAPITest02 {
public static void main(String[] args) {
List<Employee> employees = EmployeeData.getEmployees();
// 创建 stream 对象
Stream<Employee> stream = employees.stream();
// 获取到一个关于 Employee 对象中的 有关 name 属性的 Stream 对象
Stream<String> stringStream = stream.map(e -> e.getName());
// 获取到该 Stream 对象中 name 长度大于 3 的名字
Stream<String> stringFilter = stringStream.filter(e -> e.length() > 3);
stringFilter.forEach(System.out::println);

}

}

在这里插入图片描述

3.3 排序

  • Stream sorted() 产生一个新流,其中按自然顺序排序
  • Stream sorted(Comparator<? super T> comparator) 产生一个新流,其中按比较器顺序排序(也就是定制排序)。

注意:这里的排序要排序的元素信息,必须实现 Comparable 接口或者是 Comparator 定制排序 ,不然报异常,关于这部分排序内容,想要多加了解的,可以移步至:🔜🔜🔜 比较器: Comparable 与 Comparator 区别 _ChinaRainbowSea 的博客 -CSDN 博客

举例:soreted() 运用自然排序 ,排序的元素实现了 comparable 接口


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

public class StreamAPITest02 {
public static void main(String[] args) {
// sorted -- 自然排序
// 注意排序:需要实现 Comparable 接口
// 注意泛型不能放基本数据类型
List<Integer> list = Arrays.asList(12,56,3,2,1);
// 创建 Stream 对象
Stream<Integer> stream = list.stream();
stream.forEach(System.out::println);
}
}

在这里插入图片描述

举例: sorted(comparator com) 定制排序 通过年龄排序,默认是升序的 > 0 返回 1


import day33.java.Employee;
import day33.java.EmployeeData;

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

public class StreamAPITest02 {
public static void main(String[] args) {
// sorted(comparator com) 定制排序 通过年龄排序,默认是升序的 > 0 返回 1,
List<Employee> employees = EmployeeData.getEmployees();
// 创建 Stream 对象
Stream<Employee> stream = employees.stream();
Stream<Employee> sorted = stream.sorted((e1, e2) -> {
int compare = Integer.compare(e1.getAge(), e2.getAge()); // 年龄排序

        <span class="hljs-keyword">if</span> (compare != <span class="hljs-number">0</span>) {
            <span class="hljs-keyword">return</span> compare;
        } <span class="hljs-keyword">else</span> { <span class="hljs-comment">// 年龄一致,再通过比较器进一步排序,比较薪资排序</span>
            <span class="hljs-keyword">return</span> Double.compare(e1.getSalary(), e2.getSalary());
        }
    });
    sorted.forEach(System.out::println);

}

}

在这里插入图片描述

4. Stream 的终止操作

终端操作会从流水线 生成的结果。其结果可以是任何不是流的值,例如:list,Integer,甚至是 void

注意: Stream 流一旦执行了终止操作后,就不能再使用了。

4.1 匹配与查找

  • allmathc() 检查 Stream 流中内容中是否匹配所有元素
boolean allMatch(Predicate<? super T> predicate); // 检查是否匹配所有元素

import day33.java.Employee;
import day33.java.EmployeeData;

import java.util.List;

public class StreamAPITest03 {
/**
* allMatch(Predicate p) 检查是否匹配所有元素。
* 练习: 是否所有的员工的年龄都大于 18
*/
public static void main(String[] args) {
List<Employee> list = EmployeeData.getEmployees();

    boolean b = list.stream().anyMatch(e -&gt; e.getAge() &gt; 18);
    System.out.println(b);

}

}

在这里插入图片描述

  • anyMatch(Predicate p) 检查 Stream 流中内容中是否至少匹配一个元素。
boolean anyMatch(Predicate<? super T> predicate); 
import day33.java.Employee;
import day33.java.EmployeeData;

import java.util.List;

public class StreamAPITest03 {
/**
* anyMath(Predicate p) 检查是否至少匹配一个元素:
* 练习: 是否存在元素的工资大于 10000
*/

public static void main(String[] args) {
List<Employee> list = EmployeeData.getEmployees();
boolean b = list.stream().anyMatch(e -> e.getSalary() > 1000);
System.out.println(b);

}

}

在这里插入图片描述

  • noneMatch(Predicate p) 检查 Stream 流中内容中是否没有匹配所有元素
boolean noneMatch(Predicate<? super T> predicate);

import day33.java.Employee;
import day33.java.EmployeeData;

import java.util.List;
import java.util.stream.Stream;

public class StreamAPITest03 {
/**
* noneMatch(Predicate p) 检查是否没有匹配的元素。
* 练习: 是否存在员工姓 "雷"
*/

public static void main(String[] args) {
List<Employee> list = EmployeeData.getEmployees();
Stream<Employee> stream = list.stream();
// String 中的 startsWith 表示该字符串中是否含有该字符内容,有返回 true, 没有返回 fasle
boolean b = stream.noneMatch(e -> e.getName().startsWith("雷"));
System.out.println(b);
}
}

在这里插入图片描述

  • findFirst() 返回 Stream 流中内容的第一个元素

import day33.java.Employee;
import day33.java.EmployeeData;

import java.util.List;
import java.util.Optional;
import java.util.stream.Stream;

public class StreamAPITest03 {
/**
* findFirst 返回第一个元素
*/

public static void main(String[] args) {
List<Employee> list = EmployeeData.getEmployees();
Stream<Employee> stream = list.stream();
Optional<Employee> first = stream.findFirst();
System.out.println(first);

}

}

在这里插入图片描述

  • findAny() 返回当前 Stream 流中内容中中任意元素

import day33.java.Employee;
import day33.java.EmployeeData;

import java.util.List;
import java.util.Optional;
import java.util.stream.Stream;

public class StreamAPITest03 {
/**
* findAny 返回当前流中的任意元素
*/

public static void main(String[] args) {
List<Employee> list = EmployeeData.getEmployees();
Stream<Employee> stream = list.stream();
Optional<Employee> any = stream.findAny();
System.out.println(any);
}
}

在这里插入图片描述

  • count() 返回 Stream 流中内容中中元素总数

import day33.java.Employee;
import day33.java.EmployeeData;

import java.util.List;
import java.util.Optional;
import java.util.stream.Stream;

public class StreamAPITest03 {
/**
* count 返回流中元素的总个数
*/

public static void main(String[] args) {
List<Employee> list = EmployeeData.getEmployees();
Stream<Employee> stream = list.stream();
long count = stream.count();
System.out.println(count);
}
}

在这里插入图片描述

  • max(Comparator c) 返回 Stream 流中内容中中最大值

import day33.java.Employee;
import day33.java.EmployeeData;

import java.util.List;
import java.util.Optional;
import java.util.stream.Stream;

public class StreamAPITest03 {
/**
* max(Comparator c) 返回流中最大值
* 练习返回最高的工资。
*/

public static void main(String[] args) {
List<Employee> list = EmployeeData.getEmployees();
Stream<Employee> stream = list.stream();
// 创建一个有关于 Employee 对象的 属性为 salary 的 Stream 流
Stream<Double> doubleSalary = stream.map(e -> e.getSalary());
Optional<Double> max = doubleSalary.max(Double::compareTo);// 方法引用
System.out.println(max);
}
}

在这里插入图片描述

  • min(Comparator c) 返回 Stream 流中内容中中最小值

import day33.java.Employee;
import day33.java.EmployeeData;

import java.util.List;
import java.util.Optional;
import java.util.stream.Stream;

public class StreamAPITest03 {
/**
* max(Comparator c) 返回流中最大值
* 练习返回最低的工资。
*/

public static void main(String[] args) {
List<Employee> list = EmployeeData.getEmployees();
Stream<Employee> stream = list.stream();
// 创建一个有关于 Employee 对象的 属性为 salary 的 Stream 流
Stream<Double> doubleSalary = stream.map(e -> e.getSalary());
Optional<Double> min = doubleSalary.min(Double::compareTo);// 方法引用
System.out.println(min);
}
}

在这里插入图片描述

  • forEach(Consumer c) 内部迭代(使用 Collection ) 接口需要用户去做迭代,称为 外部迭代。相反 ,Stream API 使用内部迭代—— 它帮你把迭代做了
import day33.java.Employee;
import day33.java.EmployeeData;

import java.util.List;
import java.util.Optional;
import java.util.stream.Stream;

public class StreamAPITest03 {

<span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">main</span><span class="hljs-params">(String[] args)</span> {
    List&lt;Employee&gt; list = EmployeeData.getEmployees();
    list.stream().forEach(System.out::println);

    System.out.println(<span class="hljs-string">"****************************"</span>);
    <span class="hljs-comment">// 集合的遍历操作</span>
    list.forEach(System.out::println);
}

}

在这里插入图片描述

4.2 归约

如下是关于归约常用方法:

  • reduce(BinaryOperator b) 可以将流中元素结合起来,比如: sum,count 得到一个值。返回 Optional
Optional<T> reduce(BinaryOperator<T> accumulator); // 
  • reduce(T iden,BinaryOperator b) 可以将流中元素反复结合起来,得到一个值。返回 T
T reduce(T identity, BinaryOperator<T> accumulator);  // 

补充: mapreduce 的连接通常为 map-reduce 模式,因 Google 用它来进行网络搜索而出名。

举例:


import day33.java.Employee;
import day33.java.EmployeeData;

import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.stream.Stream;

public class StreamAPITest03 {
/**
* reduce(T identity, BinaryOperator) 可以将流中元素反复结合起来,得到一个值。返回
* 练习: 计算 1-10 的自然数的和
*/

public static void main(String[] args) {
List<Integer> list = Arrays.asList(1,2,3,4,5);
Stream<Integer> stream = list.stream();
Integer reduce = stream.reduce(0, Integer::sum);
System.out.println(reduce);
}
}

在这里插入图片描述

举例:


import day33.java.Employee;
import day33.java.EmployeeData;

import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.stream.Stream;

public class StreamAPITest03 {
/**
* reduce(BinaryOperator) 可以将流中元素反复的结合起来,得到一个值,返回 Optional<T>
* 练习: 计算公司所有员工的工资的总和
*/

public static void main(String[] args) {
List<Employee> list = EmployeeData.getEmployees();
// 获取到 Employees 员工工资的 Stream 流对象
Stream<Double> streamSalary = list.stream().map(e -> e.getSalary());
Optional<Double> reduce = streamSalary.reduce((d1, d2) -> d1 + d2);
System.out.println(reduce);
}
}

在这里插入图片描述

4.3 收集

Collect(Collectior c) : 将流转换为其他形式,接收一个 Collector 接口的实现,用于 Stream 中元素做汇总的方法。

<R,A> R collect(Collector<? super T,A,R> collector);

Collector 接口中方法的实现决定了如何对流执行收集的操作(如收集到 List ,Set,Map)。另外,Collectior 实用类提供了很多静态方法,可以方便创建常见收集器实例。具体方法与实例如下表:

在这里插入图片描述

在这里插入图片描述


import day33.java.Employee;
import day33.java.EmployeeData;

import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class StreamAPITest03 {

<span class="hljs-comment">/**
 * collect(Collector c) 将流转换为其他形式,接收一个Collector 接口的实现,
 * 用于给 Stream 中
 * 练习1 查找工资大于 6000 的员工,结果返回为一个 List 或 set
 */</span>
<span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">main</span><span class="hljs-params">(String[] args)</span> {
    List&lt;Employee&gt; list = EmployeeData.getEmployees();
    <span class="hljs-comment">// 获取到一个工资大于 6000 的 Stream 流</span>
    Stream&lt;Employee&gt; employeeStream = list.stream().filter(e-&gt;e.getSalary() &gt; <span class="hljs-number">6000</span>);
    List&lt;Employee&gt; collect = employeeStream.collect(Collectors.toList());
    collect.forEach(System.out::println);
}

}

在这里插入图片描述

5.1 Optional 类

在这里插入图片描述

在这里插入图片描述

到目前为止,臭名昭著的空指针异常是导致 Java 应用程序失败的最常见原因。以前,为了解决空指针异常,Google 公司著名的 Guava 项目引入了Optional类,Guava通过使用检查空值的方式来防止代码污染,它鼓励程序员写更干净的代 码。受到 Google Guava 的启发,Optional 类已经成为Java 8 类库的一部分。

  • Optional 类 (java.util.Optional) 是一个容器类,它可以保存类型 T 的值,代表这个值存在。或者仅仅保存 null,表示这个值不存在。原来用 null 表示一个值不 存在,现在 Optional 可以更好的表达这个概念。并且可以避免空指针异常。

  • Optional类的Javadoc描述如下:这是一个可以为 null 的容器对象。如果值存在 则 isPresent()方法会返回 true,调用 get() 方法会返回该对象。

Optional 提供很多有用的方法,这样我们就不用显式进行空值检测

5.2 创建 Optional 的三种方式

创建Optional类对象的方法:

  • Optional.of(T t) : 创建一个 Optional 实例,t 必须非空。

在这里插入图片描述

  • Optional.empty() : 创建一个空的 Optional 实例。
import java.util.Optional;

public class OptionalTest {
public static void main(String[] args) {
Optional<Girl> optional = Optional.empty();
System.out.println(optional);
}
}

在这里插入图片描述

  • Optional.ofNullable(T t): t 可以为 null。

import java.util.Optional;

public class OptionalTest {
public static void main(String[] args) {
Girl girl = new Girl();
girl = null;
Optional<Girl> optional = Optional.ofNullable(girl); // ofNullable 中的 t 参数可以为空
System.out.println(optional);
}
}

在这里插入图片描述

5.3 Optional 类中其他常用的方法

判断 Optional 容器中是否包含对象:

  • boolean isPresent() : 判断是否包含对象

  • void ifPresent(Consumer consumer) : 如果有值,就执行 Consumer 接口的实现代码,并且该值会作为参数传给它。

获取 Optional 容器的对象:

  • T get(): 如果调用对象包含值,返回该值,否则抛异常

  • T orElse(T other) : 如果有值则将其返回,否则返回指定的 other 对象。

  • T orElseGet(Supplier other) : 如果有值则将其返回,否则返回由 Supplier 接口实现提供的对象。

  • T orElseThrow(Supplier exceptionSupplier) : 如果有值则将其返 回,否则抛出由 Supplier 接口实现提供的异常。


import java.util.Optional;

public class OptionalTest {
// orElse(T t) 如果单前的 Optional 内部封装的 t 是非空的,则返回内部的 t,
// 如果内部的 t 是空的,则返回 orElse() 方法中的参数 t1.
// 使用 Optional 类的 getGirName()
public static String getGirName2(Girl girl) {
Optional<Girl> optional = Optional.ofNullable(girl);
// 如果 Optional 中的 girl 为 null ,则使用 如下的 new Girl(new Boy("肖战")) 替换就不为空了
// 不为 null 是不会发生替换的,使用原来的就可以了。
Girl girl2 = optional.orElse(new Girl(new Boy("肖战")));
Boy boy = girl2.getBoy();

    <span class="hljs-comment">/*Optional&lt;Boy&gt; boyOptional = Optional.ofNullable(boy);
    Boy boy1 = boyOptional.orElse(new Boy("王一博"));*/</span>

    <span class="hljs-keyword">return</span> boy.getName();
}

<span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">main</span><span class="hljs-params">(String[] args)</span> {
    <span class="hljs-type">Girl</span> <span class="hljs-variable">girl</span> <span class="hljs-operator">=</span> <span class="hljs-literal">null</span>;
    <span class="hljs-type">String</span> <span class="hljs-variable">girName2</span> <span class="hljs-operator">=</span> getGirName2(girl);
    System.out.println(girName2);

    <span class="hljs-type">Girl</span> <span class="hljs-variable">girl2</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">Girl</span>(<span class="hljs-keyword">new</span> <span class="hljs-title class_">Boy</span>(<span class="hljs-string">"王一博"</span>));
    <span class="hljs-type">String</span> <span class="hljs-variable">girName</span> <span class="hljs-operator">=</span> getGirName2(girl2);
    System.out.println(girName);

}

}

在这里插入图片描述

6. 总结:

  1. Stream 是数据渠道,用于操作数据源(集合,数组等)所生成的元素序列,“集合讲的是数据,Stream 讲的是计算”
  2. Stream 的操作三个步骤:创建 Strem 流,中间操作,终止操作。
  3. Stream 创建的四种方式
  4. stream 和集合中的迭代器是一样的,不可多次不同结构的使用。 再次使用时需要新建一个 stream 对象,才能使用。简单的来说:就是 stream 对象一次只能对应一次操作,再进行一个新的操作时,必须新建一个 stream 对象才行。不然报java.lang.IllegalStateException异常。
  5. Stream 流一旦执行了终止操作后,就不能再使用了。
  6. Stream 自己不会存储元素。
  7. Stream 不会改变源对象,相反,他们会返回一个持有结果的新 Stream 。这一点和 String 不可变的特点类似。
  8. Stream 操作是延迟执行的。这意味着它们会等到需要结果的时候才执行。
  9. Optional 类 (java.util.Optional) 是一个容器类,它可以保存类型 T 的值,代表这个值存在。或者仅仅保存 null,表示这个值不存在。原来用 null 表示一个值不 存在,现在 Optional 可以更好的表达这个概念。并且可以避免空指针异常。

7. 最后:

限于自身水平,其中存在的错误,希望大家给予指教,韩信点兵——多多益善,谢谢大家,江湖再见,后会有期 !!!

在这里插入图片描述