Java Stream 的操作这么多,其实只有两大类,看完这篇就清晰了

2024-06-13 13:50:26 浏览数 (2)

我们在前面几篇关于 Java 集合框架中 List、Set、Map 这些容器的文章中,已经给大家演示过一些 Stream 操作了,这篇文章给大家详细梳理

由于 Stream 提供的操作过多,本节内容是 Stream API 中常用操作的学习和理解,下面会专门再有一篇文章介绍在项目开发中那些高频使用的,利用 Stream 处理对象集合的使用示例。

本文大纲如下:

Java Stream 的操作这么多,其实只有两大类,看完这篇就清晰了Java Stream 的操作这么多,其实只有两大类,看完这篇就清晰了

Java 的 Stream API 提供了一种处理对象集合的函数式方法。 Stream 是和 Lambda 表达式等其他几个函数式编程特性一起在 Java 8 被引入的。这个篇教程将解释 Stream API 提供的这些函数式方法是如何工作的,以及怎么使用它们。

注意,Java 的 Stream API 与 Java IO 的 InputStream 和 OutputStream 没有任何关系,不要因为名字类似造成误解。 InputStream 和 OutputStream 是与字节流有关,而 Java 的 Stream API 用于处理对象流。

Stream 的定义

Java 的 Stream 是一个能够对其元素进行内部迭代的组件,这意味着它可以自己迭代其元素。相反地,当我们使用 Collection 的迭代功能,例如,从 Collection 获取Iterator 或者使用 Iterable 接口 的 forEach 方法这些方式进行迭代时,我们必须自己实现集合元素的迭代逻辑。

当然集合也支持获取 Stream 完成迭代,这些我们在介绍集合框架的相关章节都介绍过。

流处理

我们可以将 Listener 方法或者叫处理器方法附加到 Stream 上。当 Stream 在内部迭代元素时,将以元素为参数调用这些处理器。Stream 会为流中的每个元素调用一次处理器。所以每个处理器方法都可以处理 Stream 中的每个元素,我们把这称为流处理。

流的多个处理器方法可以形成一个调用链。链上的前一个处理器处理流中的元素,返回的新元素会作为参数传给链中的下一个处理器处理。当然,处理器可以返回相同的元素或新元素,具体取决于处理器的目的和用途。

怎么获取流

有很多方法获取 Stream ,一般最常见的是从 Collection 对象中获取 Stream。下面是一个从 List 对象获取 Stream 的例子。

代码语言:javascript复制
List<String> items = new ArrayList<String>();

items.add("one");
items.add("two");
items.add("three");

Stream<String> stream = items.stream();    

集合对象都实现了 Collection 接口,所以通过接口里定义的 stream 方法获救获取到由集合元素构成的 Steam。

流处理的构成

在对流进行处理时,不同的流操作以级联的方式形成处理链。一个流的处理链由一个源(source),0 到多个中间操作(intermediate operation)和一个终结操作(terminal operation)完成。

  • 源:源代表 Stream 中元素的来源,比如我们上面看到的集合对象。
  • 中间操作:中间操作,在一个流上添加的处理器方法,他们的返回结果是一个新的流。这些操作是延迟执行的,在终结操作启动后才会开始执行。
  • 终结操作:终结流操作是启动元素内部迭代、调用所有处理器方法并最终返回结果的操作。

概念听起来有点模糊,我们通过流处理的例子再理解一下。

代码语言:javascript复制
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Stream;

public class StreamExamples {

    public static void main(String[] args) {
        List<String> stringList = new ArrayList<>();

        stringList.add("ONE");
        stringList.add("TWO");
        stringList.add("THREE");

        Stream<String> stream = stringList.stream();

        long count = stream
                .map((value) -> value.toLowerCase())
                .count();

        System.out.println("count = "   count);

    }
}

map() 方法的调用是一个中间操作。它只是在流上设置一个 Lambda 表达式,将每个元素转换为小写形式。而对 count() 方法的调用是一个终结操作。此调用会在内部启动迭代,开始流处理,这将导致每个元素都转换为小写然后计数。

将元素转换为小写实际上并不影响元素的计数。转换部分只是作为 map() 是一个中间操作的示例。

流的中间操作

Stream API 的中间(非终结)流操作是转换或者过滤流中元素的操作。当我们把中间操作添加到流上时,我们会得到一个新的流作为结果。下面是一个添加到流上的中间操作的示例,它的执行结果会产生一个新的流。

代码语言:javascript复制
List<String> stringList = new ArrayList<>();

stringList.add("ONE");
stringList.add("TWO");
stringList.add("THREE");
    
Stream<String> stream = stringList.stream();
    
Stream<String> stringStream =
    stream.map((value) -> value.toLowerCase());

上面例子中,流上添加的 map() 调用,此调用实际上返回一个新的 Stream 实例,该实例表示原始字符串流应用了 map 操作后的新流。 只能将单个操作添加到给定的 Stream 实例上。如果需要将多个操作链接在一起,则只能将第二个操作应用于第一个操作产生的 Stream 实例上。

代码语言:javascript复制
Stream<String> stringStream1 =
        stream.map((value) -> value.toLowerCase());

Stream<½String> stringStream2 =
        stringStream1.map((value) -> value.toUpperCase());

注意第二个 map() 调用是如何在第一个 map() 调用返回的 Stream 上进行调用的。

我们一般是将 Stream 上的所有中间操作串联成一个调用链:

代码语言:javascript复制
Stream<String> stream1 = stream
  .map((value) -> value.toLowerCase())
  .map((value) -> value.toUpperCase())
  .map((value) -> value.substring(0,3));

以 map方法为代表流间操作方法的参数,是一个函数式接口,我们可以直接用 Lambda 表达式作为这些操作的参数。所以在介绍 Lambda 的那一节我们也说过,Lambda 一般是和流操作就结合起来用的。

**参考--Java 的函数式接口: **tutorials.jenkov.com/java-functi…

下面我们说一下常用的流的中间操作。

map

map() 方法将一个元素转换(或者叫映射)到另一个对象。例如,一个字符串列表,map() 可以将每个字符串转换为小写、大写或原始字符串的子字符串,或完全不同的东西。

代码语言:javascript复制
List<String> list = new ArrayList<String>();
Stream<String> stream = list.stream();

Stream<String> streamMapped = stream.map((value) -> value.toUpperCase());

filter

filter() 用于从 Stream 中过滤掉元素。 filter 方法接受一个 Predicate (也是一个函数式接口),filter() 为流中的每个元素调用 Predicate。如果元素要包含在 filter() 返回结果的流中,则 Predicate 应返回 true。如果不应包含该元素,则 Predicate 应返回 false。

代码语言:javascript复制
Stream<String> longStringsStream = stream.filter((value) -> {
    // 元素长度大于等于3,返回true,会被保留在 filter 产生的新流中。
    return value.length() >= 3;
});

比如 Stream 实例应用了上面这个 filter 后,filter 返回的结果流里只会包含长度不小于 3 的元素。

flatMap

flatMap方法接受一个 Lambda 表达式, Lambda 的返回值必须也是一个stream类型,flatMap方法最终会把所有返回的stream合并。map 与 flatMap 方法很像,都是以某种方式转换流中的元素。如果需要将每个元素转换为一个值,则使用 map 方法,如果需要将每个元素转换为多个值组成的流,且最终把所有元素的流合并成一个流,则需要使用 flatMap 方法。 在效果上看是把原来流中的每个元素进行了“展平”

代码语言:javascript复制
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Stream;

public class StreamFlatMapExamples {

    public static void main(String[] args) {

        List<String> stringList = new ArrayList<String>();

        stringList.add("One flew over the cuckoo's nest");
        stringList.add("To kill a muckingbird");
        stringList.add("Gone with the wind");

        Stream<String> stream = stringList.stream();

        stream.flatMap((value) -> {
            String[] split = value.split(" ");
            return Arrays.asList(split).stream();
        }).forEach((value) -> System.out.println(value));

    }
}

在上面的例子中,每个字符串元素被拆分成单词,变成一个 List,然后从这个 List 中获取并返回流,flatMap 方法最终会把这些流合并成一个,所以最后用流终结操作 forEach 方法,遍历并输出了每个单词。

代码语言:javascript复制
One
flew
over
the
cuckoo's
nest
To
kill
a
muckingbird
Gone
with
the
wind

distinct

distinct() 会返回一个仅包含原始流中不同元素的新 Stream 实例,任何重复的元素都将会被去掉。

代码语言:javascript复制
List<String> stringList = new ArrayList<String>();

stringList.add("one");
stringList.add("two");
stringList.add("three");
stringList.add("one");

Stream<String> stream = stringList.stream();

List<String> distinctStrings = stream
        .distinct()
        .collect(Collectors.toList());

System.out.println(distinctStrings);

在这个例子中,元素 "one" 在一开始的流中出现了两次,原始流应用 distinct 操作生成的新流中将会丢弃掉重复的元素,只保留一个 "one" 元素。所以这个例子最后的输出是:

代码语言:javascript复制
[one, two, three]

limit

limit 操作会截断原始流,返回最多只包含给定数量个元素的新流。

代码语言:javascript复制
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Stream;

public class StreamLimitExample {

    public static void main(String[] args) {

        List<String> stringList = new ArrayList<String>();

        stringList.add("one");
        stringList.add("two");
        stringList.add("three");
        stringList.add("one");

        Stream<String> stream = stringList.stream();
        stream.limit(2)
              .forEach( element -> System.out.println(element));

    }
}

这个例子中,因为对原始流使用了 limit(2) 操作,所以只会返回包含两个元素的新流,随后使用 forEach 操作将它们打印了出来。程序最终将会输出:

代码语言:javascript复制
one
two

peek

peek() 方法是一个以 Consumer (java.util.function.Consumer,Consumer 代表的是消费元素但不返回任何值的方法) 作为参数的中间操作,它返回的流与原始流相同。当原始流中的元素开始迭代时,会调用 peek 方法中指定的 Consumer 实现对元素进行处理。

正如 peek 操作名称的含义一样,peek() 方法的目的是查看流中的元素,而不是转换它们。跟其他中间操作的方法一样,peek() 方法不会启动流中元素的内部迭代,流需要一个终结操作才能开始内部元素的迭代。

peek() 方法在流处理的 DEBUG 上的应用甚广,比如我们可以利用 peek() 方法输出流的中间值,方便我们的调试。

代码语言:javascript复制
Stream.of("one", "two", "three","four").filter(e -> e.length() > 3)
                .peek(e -> System.out.println("Filtered value: "   e))
                .map(String::toUpperCase)
                .peek(e -> System.out.println("Mapped value: "   e))
                .collect(Collectors.toList());

上面的例子会输出以下调试信息。

代码语言:javascript复制
Filtered value: three
Mapped value: THREE
Filtered value: four
Mapped value: FOUR

流的终结操作

Stream 的终结操作通常会返回单个值,一旦一个 Stream 实例上的终结操作被调用,流内部元素的迭代以及流处理调用链上的中间操作就会开始执行,当迭代结束后,终结操作的返回值将作为整个流处理的返回值被返回。

代码语言:javascript复制
long count = stream
  .map((value) -> value.toLowerCase())
  .map((value) -> value.toUpperCase())
  .map((value) -> value.substring(0,3))
  .count();

Stream 的终结操作 count() 被调用后整个流处理开始执行,最后将 count() 的返回值作为结果返回,结束流操作的执行。这也是为什么把他们命名成流的终结操作的原因。

上面例子,应用的中间操作 map 对流处理的结果并没有影响,这里只是做一下演示。

下面我们把常用的流终结操作说一下。

anyMatch

anyMatch() 方法以一个 Predicate (java.util.function.Predicate 接口,它代表一个接收单个参数并返回参数是否匹配的函数)作为参数,启动 Stream 的内部迭代,并将 Predicate 参数应用于每个元素。如果 Predicate 对任何元素返回了 true(表示满足匹配),则 anyMatch() 方法的结果返回 true。如果没有元素匹配 Predicate,anyMatch() 将返回 false。

代码语言:javascript复制
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Stream;

public class StreamAnyMatchExample {

    public static void main(String[] args) {

        List<String> stringList = new ArrayList<String>();

        stringList.add("One flew over the cuckoo's nest");
        stringList.add("To kill a muckingbird");
        stringList.add("Gone with the wind");

        Stream<String> stream = stringList.stream();

        boolean anyMatch = stream.anyMatch((value) -> value.startsWith("One"));
        System.out.println(anyMatch);

    }
}

上面例程的运行结果是 true , 因为流中第一个元素就是以 "One" 开头的,满足 anyMatch 设置的条件。

allMatch

allMatch() 方法同样以一个 Predicate 作为参数,启动 Stream 中元素的内部迭代,并将 Predicate 参数应用于每个元素。如果 Predicate 为 Stream 中的所有元素都返回 true,则 allMatch() 的返回结果为 true。如果不是所有元素都与 Predicate 匹配,则 allMatch() 方法返回 false。

代码语言:javascript复制
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Stream;

public class StreamAllMatchExample {

    public static void main(String[] args) {

        List<String> stringList = new ArrayList<String>();

        stringList.add("One flew over the cuckoo's nest");
        stringList.add("To kill a muckingbird");
        stringList.add("Gone with the wind");

        Stream<String> stream = stringList.stream();

        boolean allMatch = stream.allMatch((value) -> value.startsWith("One"));
        System.out.println(allMatch);

    }
}

上面的例程我们把流上用的 anyMatch 换成了 allMatch ,结果可想而知会返回 false,因为并不是所有元素都是以 "One" 开头的。

noneMatch

Match 系列里还有一个 noneMatch 方法,顾名思义,如果流中的所有元素都与作为 noneMatch 方法参数的 Predicate 不匹配,则方法会返回 true,否则返回 false。

代码语言:javascript复制
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Stream;

public class StreamNoneExample {

    public static void main(String[] args) {

        List<String> stringList = new ArrayList<>();

        stringList.add("abc");
        stringList.add("def");

        Stream<String> stream = stringList.stream();

        boolean noneMatch = stream.noneMatch((element) -> {
            return "xyz".equals(element);
        });

        System.out.println("noneMatch = "   noneMatch); //输出 noneMatch = true

    }
}

collect

collect() 方法被调用后,会启动元素的内部迭代,并将流中的元素收集到集合或对象中。

代码语言:javascript复制
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class StreamCollectExample {

    public static void main(String[] args) {

        List<String> stringList = new ArrayList<>();

        stringList.add("One flew over the cuckoo's nest");
        stringList.add("To kill a muckingbird");
        stringList.add("Gone with the wind");

        Stream<String> stream = stringList.stream();

        List<String> stringsAsUppercaseList = stream
                .map(value -> value.toUpperCase())
                .collect(Collectors.toList());

        System.out.println(stringsAsUppercaseList);

    }
}

collect() 方法将收集器 -- Collector (java.util.stream.Collector) 作为参数。在上面的示例中,使用的是 Collectors.toList() 返回的 Collector 实现。这个收集器把流中的所有元素收集到一个 List 中去。

count

count() 方法调用后,会启动 Stream 中元素的迭代,并对元素进行计数。

代码语言:javascript复制
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Stream;

public class StreamExamples {

    public static void main(String[] args) {

        List<String> stringList = new ArrayList<String>();

        stringList.add("One flew over the cuckoo's nest");
        stringList.add("To kill a muckingbird");
        stringList.add("Gone with the wind");

        Stream<String> stream = stringList.stream();

        long count = stream.flatMap((value) -> {
            String[] split = value.split(" ");
            return Arrays.asList(split).stream();
        }).count();

        System.out.println("count = "   count); // count = 14
    }
}

上面的例程中,首先创建一个字符串 List ,然后获取该 List 的 Stream,为其添加了 flatMap() 和 count() 操作。 count() 方法调用后,流处理将开始迭代 Stream 中的元素,处理过程中字符串元素在 flatMap() 操作中被拆分为单词、合并成一个由单词组成的 Stream,然后在 count() 中进行计数。所以最终打印出的结果是 count = 14。

findAny

findAny() 方法可以从 Stream 中找到单个元素。找到的元素可以来自 Stream 中的任何位置。且它不提供从流中的哪个位置获取元素的保证。

代码语言:javascript复制
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.stream.Stream;

public class StreamFindAnyExample {

    public static void main(String[] args) {
        List<String> stringList = new ArrayList<>();

        stringList.add("one");
        stringList.add("two");
        stringList.add("three");
        stringList.add("one");

        Stream<String> stream = stringList.stream();

        Optional<String> anyElement = stream.findAny();
        if (anyElement.isPresent()) {
            System.out.println(anyElement.get());
        } else {
            System.out.println("not found");
        }
    }
}

findAny() 方法会返回一个 Optional,意味着 Stream 可能为空,因此没有返回任何元素。我们可以通过 Optional 的 isPresent() 方法检查是否找到了元素。

Optional 类是一个可以为 null 的容器对象。如果值存在则 isPresent() 方法会返回true,调用get()方法会返回容器中的对象,否则抛出异常:NoSuchElementException

findFirst

findFirst() 方法将查找 Stream 中的第一个元素,跟 findAny() 方法一样,也是返回一个 Optional,我们可以通过 Optional 的 isPresent() 方法检查是否找到了元素。

代码语言:javascript复制
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.stream.Stream;

public class StreamFindFirstExample {

    public static void main(String[] args) {
        List<String> stringList = new ArrayList<>();

        stringList.add("one");
        stringList.add("two");
        stringList.add("three");
        stringList.add("one");

        Stream<String> stream = stringList.stream();

        Optional<String> anyElement = stream.findFirst();
        if (anyElement.isPresent()) {
            System.out.println(anyElement.get());
        } else {
            System.out.println("not found");
        }
    }
}

forEach

forEach() 方法我们在介绍 Collection 的迭代时介绍过,当时主要是拿它来迭代 List 的元素。它会启动 Stream 中元素的内部迭代,并将 Consumer (java.util.function.Consumer, 一个函数式接口,上面介绍过) 应用于 Stream 中的每个元素。 注意 forEach() 方法的返回值是 void。

代码语言:javascript复制
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Stream;

public class StreamExamples {

    public static void main(String[] args) {
        List<String> stringList = new ArrayList<String>();

        stringList.add("one");
        stringList.add("two");
        stringList.add("three");

        Stream<String> stream = stringList.stream();

        stream.forEach(System.out::println);
    }
}

注意,上面例程中 forEach 的参数我们直接用了Lambda 表达式引用方法的简写形式。

min

min() 方法返回 Stream 中的最小元素。哪个元素最小是由传递给 min() 方法的 Comparator 接口实现来确定的。

代码语言:javascript复制
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.stream.Stream;

public class StreamMinExample {

    public static void main(String[] args) {
        List<String> stringList = new ArrayList<>();

        stringList.add("abc");
        stringList.add("def");
        Stream<String> stream = stringList.stream();

        // 作为 min 方法参数的Lambda 表达式可以简写成 String::compareTo
        // Optional<String> min = stream.min(String::compareTo);
        Optional<String> min = stream.min((val1, val2) -> {
            return val1.compareTo(val2);
        });

        String minString = min.get();

        System.out.println(minString); // abc
    }
}

min() 方法返回的是一个 Optional ,也就是它可能不包含结果。如果为空,直接调用 Optional 的 get() 方法将抛出 异常--NoSuchElementException。比如我们把上面的 List 添加元素的两行代码注释掉后,运行程序就会报

代码语言:javascript复制
Exception in thread "main" java.util.NoSuchElementException: No value present
	at java.util.Optional.get(Optional.java:135)
	at com.example.StreamMinExample.main(StreamMinExample.java:21)

所以最好先用 Optional 的 ifPresent() 判断一下是否包含结果,再调用 get() 获取结果。

max

与 min() 方法相对应,max() 方法会返回 Stream 中的最大元素,max() 方法的参数和返回值跟 min() 方法的也都一样,这里就不再过多阐述了,只需要把上面求最小值的方法替换成求最大值的方法 max() 即可。

代码语言:javascript复制
Optional<String> min = stream.max(String::compareTo);

reduce

reduce() 方法,是 Stream 的一个聚合方法,它可以把一个 Stream 的所有元素按照聚合函数聚合成一个结果。reduce()方法接收一个函数式接口 BinaryOperator 的实现,它定义的一个apply()方法,负责把上次累加的结果和本次的元素进行运算,并返回累加的结果。

代码语言:javascript复制
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.stream.Stream;

public class StreamReduceExample {

    public static void main(String[] args) {
        List<String> stringList = new ArrayList<>();

        stringList.add("One flew over the cuckoo's nest");
        stringList.add("To kill a muckingbird");
        stringList.add("Gone with the wind");

        Stream<String> stream = stringList.stream();

        Optional<String> reduced = stream.reduce((value, combinedValue) -> combinedValue   "   "   value);
        // 写程序的时候记得别忘了 reduced.ifPresent() 检查结果里是否有值
        System.out.println(reduced.get());
    }
}

reduce() 方法的返回值同样是一个 Optional 类的对象,所以在获取值前别忘了使用 ifPresent() 进行检查。

streadm 实现了多个版本的reduce() 方法,还有可以直接返回元素类型的版本,比如使用 reduce 实现整型Stream的元素的求和

代码语言:javascript复制
import java.util.ArrayList;
import java.util.List;

public class IntegerStreamReduceSum {
    public static void main(String[] args) {
        List<Integer> intList = new ArrayList<>();
        intList.add(10);
        intList.add(9);
        intList.add(8);
        intList.add(7);
        Integer sum = intList.stream().reduce(0, Integer::sum);
        System.out.printf("List 求和,总和为%sn", sum);
    }
}

toArray

toArray() 方法是一个流的终结操作,它会启动流中元素的内部迭代,并返回一个包含所有元素的 Object 数组。

代码语言:javascript复制
List<String> stringList = new ArrayList<String>();

stringList.add("One flew over the cuckoo's nest");
stringList.add("To kill a muckingbird");
stringList.add("Gone with the wind");

Stream<String> stream = stringList.stream();

Object[] objects = stream.toArray();

不过 toArray 还有一个重载方法,允许传入指定类型数组的构造方法,比如我们用 toArray 把流中的元素收集到字符串数组中,可以这么写:

代码语言:javascript复制
String[] strArray = stream.toArray(String[]::new);

流的拼接

Java 的Stream 接口包含一个名为 concat() 的静态方法,它可以将两个流连接成一个。

代码语言:javascript复制
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class StreamConcatExample {

    public static void main(String[] args) {
        List<String> stringList = new ArrayList<>();

        stringList.add("One flew over the cuckoo's nest");
        stringList.add("To kill a muckingbird");
        stringList.add("Gone with the wind");

        Stream<String> stream1 = stringList.stream();

        List<String> stringList2 = new ArrayList<>();
        stringList2.add("Lord of the Rings");
        stringList2.add("Planet of the Rats");
        stringList2.add("Phantom Menace");

        Stream<String> stream2 = stringList2.stream();

        Stream<String> concatStream = Stream.concat(stream1, stream2);

        List<String> stringsAsUppercaseList = concatStream
                .collect(Collectors.toList());

        System.out.println(stringsAsUppercaseList);
    }
}

从数组创建流

上面关于 Stream 的例子我们都是从 Collection 实例的 stream() 方法获取的集合包含的所有元素的流,除了这种方法之外,Java 的 Stream 接口中提供了一个名为 of 的静态方法,能支持从单个,多个对象或者数组对象快速创建流。

代码语言:javascript复制
import java.util.stream.Stream;

public class StreamExamples {

    public static void main(String[] args) {
        Stream<String> stream1 = Stream.of("one", "two", "three");

        Stream<String> stream2 = Stream.of(new String[]{"one", "two"});

        System.out.println(stream1.count()); // 输出3
        System.out.println(stream2.count()); // 输出2
    }
}

总结

上面我们把 Stream 的两大类操作:流的中间操作、流的终结操作都有哪些方法给大家列举了一遍,让大家对 Stream 能完成的操作有了大致的印象。不过为了讲解这些操作用的都是非常简单的例子,流操作的数据也都是简单类型的,主要的目的是让大家能更快速地理解 Stream 的各种操作应用在数据上后,都有什么效果。

下一篇我会演示一些在项目开发中我们会高频用到的,使用 Stream 完成各种复杂操作的示例,让大家做项目的时候可以直接进行参考,进一步提升你用 Java 编程、开发项目的体验。

0 人点赞