主页 聊一聊Java1.8流
Post
Cancel

聊一聊Java1.8流

流表示元素序列,并支持对这些元素执行计算的不同操作。 使用Java 8的Collection接口有两种方法来生成流:Stream()和parallelStream()。流操作可以是中间操作,也可以是终端操作。中间操作返回一个流,因此可以在流关闭之前链接多个中间操作。终端操作要么无效,要么返回非流结果。

流的使用

流是一组元素序列,可以在其上执行顺序和并行聚合操作。因此,从流接收到的数据在到达时被单独处理,而不是对数据进行批处理。当与lambda表达式结合使用时,它们提供了一种使用函数方法对数据序列执行操作的简洁方法。

案例

1
2
3
4
Stream<String> fruitStream = Stream.of("apple", "banana", "pear", "kiwi", "orange");
fruitStream.filter(s -> s.contains("a"))
           .map(String::toUpperCase)
.sorted() .forEach(System.out::println);

输出:

1
2
3
4
APPLE
BANANA
ORANGE
PEAR

上述代码的运作可概括如下:

  1. 创建一个Stream<String>, 其中包含使用Stream.of(values)创造的按顺序排列的以水果命名的字符串元素流
  2. filter()操作只保留与给定条件匹配的元素。在本例中,它保留了包含“a”的元素。条件以lambda表达式给出。
  3. map()操作使用一个给定的函数(称为映射器)转换每个元素。在本例中,使用方法引用String::toUppercase将每个字符串映射到它的大写字符串版本。

    这里需要注意,如果映射函数返回与其输入参数不同的类型,map()操作将返回一个具有不同类型的流。例如,在一个Stream<String>上调用map(String::isEmpty)将返回一个Stream<Boolean>

  4. sorted()操作根据流的自然顺序对元素进行排序(在字符串的情况下,按字典顺序)
  5. 最后forEach(action)执行一个操作,该操作作用于流的每个元素,并将其传递给使用者。在本例中,每个元素都被简单地打印到控制台。该操作是一个终端操作,因此不可能再对其进行操作。

    注意,流上定义的操作是由于终端操作而执行的。如果没有终端操作,则不处理流。流不能重用。一旦调用终端操作,流对象就变得不可用。

流的关闭

流通常不必关闭。一般只需要关闭操作IO通道的流。大多数流类型不操作资源,因此不需要关闭。

Stream接口继承AutoCloseable.可以通过调用close方法或使用try- with-resource语句来关闭流。

当你从一个文件中创建一个行流时, 这个流应该被关闭:

1
2
3
try (Stream<String> lines = Files.lines(Paths.get("somePath"))) {
    lines.forEach(System.out::println);
}

Stream接口还声明了Stream. onClose()方法,该方法允许您注册一个关闭事件,当流关闭时将调用该处理程序。

1
2
3
public Stream<String> streamAndDelete(Path path) throws IOException {
    return Files.lines(path).onClose(() -> someClass.deletePath(path));
}

只有当close()方法被调用时,该事件才会执行,可以通过try-with- resources语句显式或隐式地调用close()方法。

流处理的顺序

流对象的处理可以是顺序的,也可以是并行的。

在顺序模式中,元素按流源的顺序处理。如果流是有序的(例如SortedMap的实现或List),则保证处理与源的顺序匹配。

1
2
3
4
5
6
List<Integer> integerList = Arrays.asList(0, 1, 2, 3, 42);
// 按顺序处理
long howManyOddNumbers = integerList.stream()
            .filter(e -> (e % 2) == 1)
            .count();
System.out.println(howManyOddNumbers); // 输出: 2

并行模式允许在多核上使用多线程,但不能保证处理元素的顺序。

如果在顺序流上调用多个方法,则有些方法不必调用。 例如,如果过滤了一个流,并且将元素的数量减少到一个,则不会发生对sort等方法的后续调用。这可以提高顺序流的性能而并行流没有这方面的优化。

1
2
3
4
5
// 并行流
long howManyOddNumbersParallel = integerList.parallelStream()
            .filter(e -> (e % 2) == 1)
            .count();
System.out.println(howManyOddNumbersParallel); // 输出: 2

与容器(或集合)的区别

虽然有些操作可以同时在容器和流上执行,但它们最终的用途不同,支持不同的操作。容器更关注如何存储元素以及如何有效地访问这些元素。另一方面,流不提供对其元素的直接访问和操作;它更专注于作为一个集体实体的对象组,并作为一个整体对该实体执行操作。流和集合是针对这些不同目的的独立高级抽象。

dd

只有当存在count()、collect()或forEach()等终端操作时,才会遍历流。否则,将不执行流上的任何操作。

在下面的示例中,没有向流添加终端操作,因此不会调用filter()操作,也不会产生输出,因为peek()不是终端操作。

这是一个具有有效终端操作的流序列,因此产生一个输出。你也可以用forEach代替peek:

更新于 2020-03-12 2020-03-12T12:37:36+08:00
This post is licensed under CC BY 4.0

聊一聊Java(enmu)枚举的使用

10个比较流行的PHP框架