Java 8 Stream 速学

发布于 2024-12-16 00:49:19 字数 6772 浏览 3 评论 0

Java 8 引入了便捷、高效处理一系列元素的新 API —— java.util.stream

创建流

一旦创建出 Stream 实例后,对该实例的修改并不会影响到源数据,故可以从一个源数据中创建出多个 Stream 实例。

创建空流

创建一个空的 Stream 实例:

Stream<String> streamEmpty = Stream.empty();

常用于避免返回 null 的情况:

public Stream<String> streamOf(List<String> list) {
  return list == null || list.isEmpty() ? Stream.empty() : list.stream();
}

从集合中创建流

可以从任何实现了 java.util.Collection 接口(定义了一个默认方法 stream() )的集合类(如 ListSet 等)中创建 Stream 实例:

Collection<String> collection = Arrays.asList("a", "c", "b");
Stream<String> streamOfCollection = collection.stream();

从数组中创建流

1、从数组源数据中创建流:

Stream<String> stream = Stream.of("a", "c", "b");

2、从已有数组中创建流:

String[] arr = {"a", "c", "b"};
Stream<String> streamOfArrayFull = Arrays.stream(arr); // a c b
Stream<String> streamOfArrayPart = Arrays.stream(arr, 1, 2); // c

Stream.builder()

当使用 builder 时,特定的类型应该在语句的右边部分额外指定,否则 build() 方法将创建 Stream<Object> 实例对象:

Stream<String> streamBuilder = Stream.<String>builder().add("a").add("b").add("c").build();

Stream.generate()

generate() 方法接受 Supplier<T> 进行元素生成。由于生成的流是无限的,开发人员应该指定所需的大小,否则 Generate() 方法将一直工作,直到内存限制:

Stream<String> streamGenerated = Stream.generate(() -> "element").limit(5);

Stream.iterate()

创建无限流的另一种方法是使用 iterate() 方法:

Stream<Integer> streamIterated = Stream.iterate(2, n -> n + 2).limit(3); // 2 4 6

原始类型流

Java 8 可以从三种基本类型( intlongdouble )中创建流。由于 Stream<T> 是一个泛型接口,无法使用原始类型作为泛型的类型参数,因此 Java 还提供了三个新的特殊接口: IntStreamLongStreamDoubleStream

使用新的接口可以减少不必要的自动装箱,从而提高工作效率:

IntStream intStream = IntStream.range(1, 3);
LongStream longStream = LongStream.rangeClosed(1, 3);

此外,Java 8 Random 类还提供用于生成随机数原语流的各种方法。比如:

Random random = new Random();
DoubleStream doubleStream = random.doubles(3);

字符串流

字符串也可以用作创建流的源。

1、借助 String 类的 chars() 方法。 因为 JDK 中没有 CharStream 接口,所以使用 IntStream 来表示字符流:

IntStream streamOfChars = "abc".chars();

2、根据指定的正则表达式将字符串拆分为子字符串:

Stream<String> streamOfString = Pattern.compile(", ").splitAsStream("a, b, c"); // a b c

文件流

Java NIO 类文件允许通过 lines() 方法生成文本文件的 Stream<String> 。文本的每一行都成为流的一个元素:

Path path = Paths.get("D:\\test.txt");
Stream<String> streamOfStrings = Files.lines(path);
Stream<String> streamWithCharset = Files.lines(path, StandardCharsets.UTF_8);

多线程流

从任何实现了 java.util.Collection 接口(定义了一个默认方法 parallelStream() )的类中获取多线程(并行模式)流。比如:

List<String> list = new ArrayList<String>() {{
  add("a");
  add("c");
  add("b");
}};
Stream<String> stream = list.parallelStream();

操作流

流中的操作分为两类:

  • 中间操作:返回 Stream<T> ,允许链式调用
  • 终端操作:返回最终结果,不可再被该流进行后续操作

注意:对流的操作只会影响该实例化的流,而不影响源数据。

遍例

替代 forfor-eachwhile 循环语句。比如:

List<String> list = new ArrayList<String>() {{
  add("t_a");
  add("t_c");
  add("b");
}};

for (String item : list) {
  if (item.startsWith("t_")) {
    System.out.println(item);
  }
}

将上面的 for-each 更改为 Java 8 Stream 代码:

list.stream().filter(item -> item.startsWith("t_")).forEach(System.out::println);

过滤

filter() :通过指定条件来过滤流中的元素。比如:

List<String> names = new ArrayList<String>() {{
  add("Jason");
  add("Jason Wu");
  add("Bruce");
  add("Bruce Lee");
}};

names.stream().filter(element -> element.contains("Jason")).forEach(System.out::println);

映射

1、 map() :对流中的元素应用某个特定函数,然后再将这些函数结果组装为一个新的流。比如:

String[] numbers = new String[]{"1", "3", "2"};
Stream<Integer> stream = Arrays.stream(numbers).map(Integer::parseInt);

2、 flatMap() :从流中元素值组装出一个新的流。比如:

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

public class LearnTest {

  public static void main(String[] args) {

    List<User> users = new ArrayList<User>() {{
      add(new User("Jason", "Java", "JS"));
      add(new User("Bruce", "Python", "Go"));
    }};

    Stream<String> stream = users.stream().flatMap(user -> user.hobbies.stream());
    stream.forEach(System.out::println);
  }

  static class User {

    private String userName;
    private List<String> hobbies;

    public User(String userName, String... hobbies) {
      this.userName = userName;
      this.hobbies = new ArrayList<>(Arrays.asList(hobbies));
    }
  }
}

匹配

anyMatch()allMatch()noneMatch() 方法已命名自解释。比如:

boolean isValid = list.stream().anyMatch(element -> element.contains("h")); // true
boolean isValidOne = list.stream().allMatch(element -> element.contains("h")); // false
boolean isValidTwo = list.stream().noneMatch(element -> element.contains("h")); // false

缩减操作

reduce() :第一个参数为起始值,第二个参数为累加器函数。比如:

List<Integer> integers = Arrays.asList(1, 1, 1);
Integer reduced = integers.stream().reduce(23, Integer::sum); // 26

或多线程流下:

int reduced = Arrays.asList(1, 2, 3).parallelStream().reduce(10, (a, b) -> {
  System.out.println("Accumulator was called: " + (a + b));
  return a + b;
}, (a, b) -> {
  System.out.println("Combiner was called: " + (a + b));
  return a + b;
});

System.out.println(reduced);

// Accumulator was called: 12
// Accumulator was called: 13
// Combiner was called: 25
// Accumulator was called: 11
// Combiner was called: 36
// 36

收集器

collect() :将流转换为 CollectionMapString 。其中工具类 Collectors 提供了一些常用的解决方案。

List<String> list = new ArrayList<String>() {{
  add("js");
  add("edm");
}};

String str = list.stream().map(String::toUpperCase).collect(Collectors.joining(","));

关闭流

在实际开发中,我们一定要注意最后关闭流(可通过 close() 方法,或执行终端操作)。因为不及时关闭实例化的流,可能会导致内存泄漏。

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据

关于作者

酒废

暂无简介

文章
评论
24 人气
更多

推荐作者

迎风吟唱

文章 0 评论 0

qq_hXErI

文章 0 评论 0

茶底世界

文章 0 评论 0

捎一片雪花

文章 0 评论 0

文章 0 评论 0

    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文