Java-Stream入门

学习Stream的目的

  • 函数式编程渐渐变成主流,而Stream是函数式编程的重点。
  • 相对于传统的编程方式,代码更为简洁清晰易懂。
  • 使得并发编程变得如此简单。
  • 有效的避免了代码嵌套地狱。(见样例)
if (条件1) {
    if (条件2) {
        if (条件3) {
            // 再嵌套下去都快见到Diablo了。
        }
    }
}

Stream的特点

  • 不修改数据源:任何对于Stream对象的操作都不会修改数据源的数据。
  • 延迟执行:中间操作只是对于一系列操作细节的定义,而非执行。只有在终端操作被调用的同时执行中间操作。
  • 可消费:任何一个Stream对象执行了终端操作后都将不可再利用。只能操作由数据源生成的新的Stream。

Stream的分类

  • 串行流:单项环境下的Stream,基础。
List.of().stream(); // 获取串行流
  • 并行流:多线程环境下的Stream,重点难点。
List.of().parallelStream(); // 获取并行流

Stream对象的创建

总共有 三种方式:

  • 经由集合对象创建
List list = new ArrayList<>();
Stream stream = list.stream(); // 创建串行流
Stream stream = list.parallelStream(); // 创建并行流
  • 经由数组对象创建(两种)
String[] strs = new String[10];
// 将数组所有元素装入stream
Stream stream1 = Arrays.stream(strs);
// 将数组指定区间的元素装入stream
Stream stream2 = Arrays.stream(strs, 1, 7);
  • 使用Stream的静态方法创建(三种)
// 由单个元素创建Stream,元素不允许为null
Stream stream1 = Stream.of("Test");
// 由单个元素创建stream,元素允许为null
Stream stream2 = Stream.ofNullable(null);
// 由多个元素创建stream,内部其实调用的是Arrays.stream(T[] array)
Stream stream3 = Stream.of("Test1", "Test2", "Test3");

方法的分类

  • 中间操作:根据调用的方法,返回各种各样的stream对象。传入的各种Lambda只是修改了该对象中对应方法的定义,而非执行。无并行流专用的方法。
  • 终端操作:执行终端操作的方法,并且其间也执行中间操作对应的方法。有并行流专用的方法。

方法签名: Stream<t> distinct()</t>
作用:返回一个去重后的Stream。

List list = List.of("1", "1");
list.stream().distinct()
    .forEach(t -> System.out.println(t)); // 输出:1

方法签名: Stream<t> filter(Predicate<? super T> predicate)</t>
作用:返回一个由满足predicate条件的元素构成的Stream。

List list = List.of(1, 3, 5);
list.stream().filter(t -> t >= 3)
    .forEach(t -> System.out.println(t)); // 输出:3, 5

因为sorted存在两种重载,并且在jdk源码的实现并不相同,所以我们分开讨论。

方法签名: Stream<t> sorted()</t>
作用:通过调用T类型重写Comparable接口的compareTo方法排序,返回排序后的Stream。

// 此处省略了一些java文件定义的结构,请着眼于一下核心代码
// NG例, 不实现Comparable接口
class MyInteger {
    int value;
    MyInteger(int value) {
        this.value = value;
    }
}
List list = new ArrayList<>();
list.add(new MyInteger(4));
list.add(new MyInteger(1));
list.add(new MyInteger(3));
list.stream().sorted() // 不报错
    .forEach(t -> System.out.println(t.value)); // ClassCastException:不能被强转成Comparable类型

// OK例, 实现Comparable接口(Integer实现了Comparable接口)
List list = new ArrayList<>();
list.add(4);
list.add(1);
list.add(3);
list.stream().sorted()
    .forEach(t -> System.out.println(t)); // 输出:1 3 4

方法签名: Stream<t> sorted(Comparator<? super T> comparator)</t>
作用:通过调用传入的comparator的compare方法排序,返回排序后的Stream。

List list = new ArrayList<>();
list.add(4);
list.add(1);
list.add(3);
list.stream().sorted((o1, o2) -> o2 - o1) // 传入一个比较器的实现
    .forEach(t -> System.out.println(t.value)); // 输出:4 3 1

方法签名: Stream<t> skip(long n)</t>
作用:返回一个不包含前n项的Stream。

List list = new ArrayList<>();
list.add(4);
list.add(1);
list.add(3);
list.stream().skip(2) // 跳过前两个元素
    .forEach(t -> System.out.println(t.value)); // 输出:3

方法签名: Stream<t> limit(long n)</t>
作用:返回一个包含前n项的Stream。

List list = new ArrayList<>();
list.add(4);
list.add(1);
list.add(3);
list.stream().limit(2) // 跳过前两个元素
    .forEach(t -> System.out.println(t.value)); // 输出:4 1

方法签名: Stream<t> peek(Consumer<? super T> action)</t>
作用:执行消费操作,返回原Stream。虽然有消费操作,但是Stream的状态并不会改变。并不会真正消费Stream这一特点是的peek方法常用于调试。

List list = new ArrayList<>();
list.add(4);
list.add(1);
list.add(3);
Stream stream = list.stream();
stream.peek(t -> System.out.println(t)); // 输出:4 1 3, 并且不消费stream
stream.forEach(t -> System.out.println(t)); // 输出:4 1 3, 并且消费stream(消费后stream不可再次使用)
stream.forEach(t -> System.out.println(t)); // IllegalStateException:stream已经被操作或关闭

方法签名: <r> Stream<r> map(Function<? super T, ? extends R> mapper)</r></r>
作用:将原Stream中的所有元素类型从T转化为R(不是强转,是通过一些操作得到R类型),返回封装R类型元素的Stream。

List list = new ArrayList<>();
list.add(4);
list.add(1);
list.add(3);
list.stream().map(t -> String.valueOf(t)) // 将元素都转换成String
    .forEach(t -> System.out.println(t)); // 输出:4 1 3

方法签名: <r> Stream<r> flatMap(Function<? super T, ? extends Stream<? extends R>> mapper)</r></r>
作用:将原Stream中的所有元素类型从T转化为R,返回Stream

List list = new ArrayList<>();
list.add(new Integer[] {1, 2, 3});
list.add(new Integer[] {4, 5, 3});
list.stream().flatMap(t -> Stream.of(t)) // 将各个数组中的元素都放入stream, 实现 1 → n 的转换
    .forEach(t -> System.out.println(t)); // 输出:1 2 3 4 5 3

方法签名: void forEach(Consumer<? super T> action)
作用:迭代消费Stream里的所有元素,比如打印,写入文件,写入DB等。

List list = new ArrayList<>();
list.add(4);
list.add(1);
list.add(3);
list.stream().forEach(t -> System.out.println(t)); // 输出:4 1 3

方法签名: Object[] toArray()
作用:将Stream中的所有元素封装成Object[]返回。

List list = new ArrayList<>();
list.add(4);
list.add(1);
list.add(3);
Object[] objects = list.stream().toArray(); // 与元素类型无关, 固定返回Object[]
Integer[] ints = (Integer[])objects; // 使用时你可能需要类型强转

方法签名: long count()
作用:返回Stream中的元素个数。

List list = new ArrayList<>();
list.add(4);
list.add(1);
list.add(3);
long count = list.stream().count(); // 返回stream中的元素个数, 当前为3

方法签名: Optional<t> findFirst()</t>
作用:Stream的第一个元素封装在Optional中返回。

List list = new ArrayList<>();
list.add(4);
list.add(1);
list.add(3);
Optional first = list.stream().findFirst(); // 返回stream中的第一个元素, 当前为4
/*
    Optional类内容比较多所以现在不做赘述, 大家姑且就认为是个只能存放一个元素的容器就好,以后会开一个新的博文详细为    大家讲解用法
 */

方法签名: boolean anyMatch(Predicate<? super T> predicate)
作用:当存在符合predicate条件的元素时返回true,否则返回false。

List list = new ArrayList<>();
list.add(4);
list.add(1);
list.add(3);
boolean bool = list.stream().anyMatch(t -> t

方法签名: boolean allMatch(Predicate<? super T> predicate)
作用:当所有元素都符合predicate条件时返回true,否则返回false。

List list = new ArrayList<>();
list.add(4);
list.add(1);
list.add(3);
boolean bool = list.stream().allMatch(t -> t

方法签名: boolean noneMatch(Predicate<? super T> predicate)
作用:当所有元素都不符合predicate条件时返回true,否则返回false。

List list = new ArrayList<>();
list.add(4);
list.add(1);
list.add(3);
boolean bool = list.stream().noneMatch(t -> t

方法签名: Optional<t> min(Comparator<? super T> comparator)</t>
作用:按照传入的比较器将最小的元素封装在Optional中返回。

List list = new ArrayList<>();
list.add(4);
list.add(1);
list.add(3);
Optional min = list.stream().min((o1, o2) -> o2 - o1); // 根据传入的比较器实现返回最小值, 当前4
/*
    这个方法可以认为stream按照传入的比较器排序, 返回排序后的第一个元素
 */

方法签名: Optional<t> max(Comparator<? super T> comparator)</t>
作用:按照传入的比较器将最大的元素封装在Optional中返回。

List list = new ArrayList<>();
list.add(4);
list.add(1);
list.add(3);
Optional max = list.stream().max((o1, o2) -> o2 - o1); // 根据传入的比较器实现返回最大值
/*
    这个方法可以认为stream按照传入的比较器排序, 返回排序后的最后一个元素
 */

由于有两种重载,所有分开说。

方法签名: <r, a> R collect(Collector<? super T, A, R> collector)</r,>
作用:迭代消费Stream里的所有元素,返回一个R类型的容器。

List list = new ArrayList<>();
list.add(4);
list.add(1);
list.add(3);
Set set = list.stream().collect(Collectors.toSet()); // 将所有元素封装成Set返回, 元素类型不变
/*
    由于Collector并不是一个函数式接口,所以我们在使用过程中必须传入一个实现类或者匿名内部类(不推荐,代码太长),而不能使用Lambda表达式的形式。
    而常用的Colloector的实现类中就有Collectors,这是个提供类型转换功能的类,可以将元素转换为各种Collection或Map。
 */

方法签名: <r> R collect(Supplier<r> supplier, BiConsumer<r, ? super t> accumulator, BiConsumer<r, r> combiner)</r,></r,></r></r>
作用:开启多个线程,各线程分别累加,最后合并各线程结果。

推荐使用并行流调用本方法。

List list = new ArrayList<>();
list.add(4);
list.add(1);
list.add(3);
List collect = list.parallelStream() // 由于线程合并操作涉及到多线程所以使用并行流,否则将不执行线程合并操作
                .collect(() -> new ArrayList(), // 创建一个ArrayList对象, 每个线程都会执行
                (r, t) -> r.add(t), // 向上面创建的List里追加t(原本list中的元素), 每个线程都会执行
                (r1, r2) -> r1.addAll(r2)); // 将两个线程的List合并到一起,返回。
/*
    这里开启线程与合并线程结果的逻辑与归并排序非常类似
    由于原数组中有三个元素, 所以最终开启三个线程
    线程1: [4]
    线程2: [1]
    线程3: [3]
    接下来是线程合并:
    第一轮:
        线程1: 线程1的结果 + 线程2的结果 -> [4]
        线程2: 线程2的结果 + 线程3的结果 -> [1, 3]
    第二轮:
        线程1: 线程1的结果 + 线程2的结果 -> [4, 1, 3]
    返回线程1的结果
 */

由于有三种重载,所有分开说。

与sql中的聚合函数比较相似,都是计算多个数据返回一个结果,实现 n → 1。

方法签名: T reduce(T identity, BinaryOperator<t> accumulator)</t>
作用:初始值identity与所有的元素都计算一遍,返回最终结果T。

List list = new ArrayList<>();
list.add(4);
list.add(1);
list.add(3);
Integer sum = list.stream().reduce(10, (t1, t2) -> t1 + t2); // 在初始值10的基础上累加没有元素的值并返回, 当前18

方法签名: Optional<t> reduce(BinaryOperator<t> accumulator)</t></t>
作用:无初始值,对所有的元素都计算一遍,将最终结果的T封装在Optional中返回。

List list = new ArrayList<>();
list.add(4);
list.add(1);
list.add(3);
Optional sum = list.stream().reduce((t1, t2) -> t1 + t2); // 返回所有元素的累加, 当前8

方法签名: <u> U reduce(U identity, BiFunction<u, ? super t, u> accumulator, BinaryOperator<u> combiner)</u></u,></u>
作用:开启多个线程,各线程分别累加,最后合并各线程结果。

推荐使用并行流调用本方法。

List list = new ArrayList<>();
        list.add("4");
        list.add("1");
        list.add("5");
        list.add("2");
Integer sum = list.parallelStream() // 由于线程合并操作涉及到多线程所以使用并行流,否则将不执行线程合并操作
    .reduce(10, // 定义初始值, 此初始值各线程独立存在
            (u, t) -> u + Integer.valueOf(t), // u: 初始值, t: 原list的元素值
            (u1, u2) -> u1 + u2); // 合并各线程的结果, 返回, 当前52
/*
    这里开启线程与合并线程结果的逻辑与归并排序非常类似
    由于原数组中有四个元素, 所以最终开启四个线程
    线程1: 10 + 4 -> 14
    线程2: 10 + 1 -> 11
    线程3: 10 + 5 -> 15
    线程4: 10 + 2 -> 12
    上面这一步省略了各线程累加的过程, 实际就是在初始值的基础上累加
    接下来是线程合并:
    第一轮:
        线程1: 线程1的结果 + 线程2的结果 -> 14 + 11 -> 25
        线程3: 线程3的结果 + 线程4的结果 -> 15 + 12 -> 27
    第二轮:
        线程1: 线程1的结果 + 线程3的结果 -> 25 + 27 -> 52
    返回线程1的结果
 */

Original: https://www.cnblogs.com/spoonb/p/16616100.html
Author: spoonb
Title: Java-Stream入门

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/684172/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球