Jave8中的stream

简介: Jave8中的stream

引言

首先,Java 8 Streams 不应与 Java I/O 流混淆(例如:FileInputStream 等);这些彼此之间几乎没有关系。

简而言之,流是数据源的包装器,允许我们使用该数据源进行操作,并使批量处理方便快捷。

Stream不存储数据,从这个意义上说,它不是数据结构。它也从不修改底层数据源

这个功能 - java.util.stream - 支持对元素流的函数式操作,例如对集合的 map-reduce 转换。

现在让我们深入研究几个流创建和使用的简单示例——在进入术语和核心概念之前。

创建一个java流

  1. 已有的list转换成流对象
private static Employee[] arrayOfEmps = {
    new Employee(1, "Jeff Bezos", 100000.0), 
    new Employee(2, "Bill Gates", 200000.0), 
    new Employee(3, "Mark Zuckerberg", 300000.0)
};
Stream.of(arrayOfEmps);
复制代码
private static List<Employee> empList = Arrays.asList(arrayOfEmps);
empList.stream();
复制代码
  1. 新建一个流
Stream.of(arrayOfEmps[0], arrayOfEmps[1], arrayOfEmps[2]);
复制代码
  1. 使用build
Stream.Builder<Employee> empStreamBuilder = Stream.builder();
empStreamBuilder.accept(arrayOfEmps[0]);
empStreamBuilder.accept(arrayOfEmps[1]);
empStreamBuilder.accept(arrayOfEmps[2]);
Stream<Employee> empStream = empStreamBuilder.build();
复制代码

Stream的操作

forEach

forEach() 是最简单最常用的操作;它遍历流元素,在每个元素上调用提供的函数。

该方法非常常见,已在 Iterable、Map 等中直接引入

@Test
public void whenIncrementSalaryForEachEmployee_thenApplyNewSalary() {    
    empList.stream().forEach(e -> e.salaryIncrement(10.0));
    assertThat(empList, contains(
      hasProperty("salary", equalTo(110000.0)),
      hasProperty("salary", equalTo(220000.0)),
      hasProperty("salary", equalTo(330000.0))
    ));
}
复制代码

forEach() 是一个终端操作,也就是说,执行完操作后,流管道就被认为是消耗掉了,不能再使用了。我们将在下一节中详细讨论终端操作。

map

map() 在对原始流的每个元素应用函数后生成一个新流。新的流可以是不同的类型。

以下示例将整数流转换为雇员流

@Test
public void whenMapIdToEmployees_thenGetEmployeeStream() {
    Integer[] empIds = { 1, 2, 3 };
    List<Employee> employees = Stream.of(empIds)
      .map(employeeRepository::findById)
      .collect(Collectors.toList());
    assertEquals(employees.size(), empIds.length);
}
复制代码

在这里,我们从数组中获取员工 ID 的整数流。每个 Integer 都被传递给函数 employeeRepository::findById()——它返回相应的 Employee 对象;这有效地形成了一个员工流。

collect

我们在前面的例子中看到了 collect() 的工作原理;一旦我们完成所有处理,这是从流中取出内容的常用方法之一:

@Test
public void whenCollectStreamToList_thenGetList() {
    List<Employee> employees = empList.stream().collect(Collectors.toList());
    assertEquals(empList, employees);
}
复制代码

collect() 对 Stream 实例中保存的数据元素执行可变折叠操作(将元素重新打包到某些数据结构并应用一些额外的逻辑,将它们连接起来等)。

此操作的策略是通过 Collector 接口实现提供的。在上面的示例中,我们使用 toList 收集器将所有 Stream 元素收集到一个 List 实例中。

filter

接下来,我们来看看 filter();这将生成一个新流,其中包含通过给定测试(由谓词指定)的原始流的元素。

让我们看看它是如何工作的:

@Test
public void whenFilterEmployees_thenGetFilteredStream() {
    Integer[] empIds = { 1, 2, 3, 4 };
    List<Employee> employees = Stream.of(empIds)
      .map(employeeRepository::findById)
      .filter(e -> e != null)
      .filter(e -> e.getSalary() > 200000)
      .collect(Collectors.toList());
    assertEquals(Arrays.asList(arrayOfEmps[2]), employees);
}
复制代码

在上面的示例中,我们首先过滤掉无效员工 ID 的空引用,然后再次应用过滤器以仅保留工资超过特定阈值的员工。

findFirst

ndFirst() 为流中的第一个条目返回一个 Optional; Optional 当然可以为空:

@Test
public void whenFindFirst_thenGetFirstEmployeeInStream() {
    Integer[] empIds = { 1, 2, 3, 4 };
    Employee employee = Stream.of(empIds)
      .map(employeeRepository::findById)
      .filter(e -> e != null)
      .filter(e -> e.getSalary() > 100000)
      .findFirst()
      .orElse(null);
    assertEquals(employee.getSalary(), new Double(200000));
}
复制代码

在这里,返回第一个工资大于 100000 的员工。如果不存在这样的员工,则返回 null。

toArray

我们看到了如何使用 collect() 从流中获取数据。如果我们需要从流中取出一个数组,我们可以简单地使用 toArray():

@Test
public void whenStreamToArray_thenGetArray() {
    Employee[] employees = empList.stream().toArray(Employee[]::new);
    assertThat(empList.toArray(), equalTo(employees));
}
复制代码

语法 Employee[]::new 创建一个空的 Employee 数组——然后用流中的元素填充。

flatMap

流可以包含复杂的数据结构,例如 Stream<List>。在这种情况下,flatMap() 可以帮助我们扁平化数据结构以简化进一步的操作:

@Test public void whenFlatMapEmployeeNames_thenGetNameStream() { List<List> namesNested = Arrays.asList( Arrays.asList("Jeff", "Bezos"), Arrays.asList("Bill", "Gates"), Arrays.asList("Mark", "Zuckerberg"));

List<String> namesFlatStream = namesNested.stream()
  .flatMap(Collection::stream)
  .collect(Collectors.toList());
assertEquals(namesFlatStream.size(), namesNested.size() * 2);
复制代码


请注意我们如何能够将 Stream<List> 转换为更简单的 Stream - 使用 flatMap() API


相关文章
|
6月前
|
Java Unix Windows
|
2月前
|
存储 Java API
Java——Stream流详解
Stream流是JDK 8引入的概念,用于高效处理集合或数组数据。其API支持声明式编程,操作分为中间操作和终端操作。中间操作包括过滤、映射、排序等,可链式调用;终端操作则完成数据处理,如遍历、收集等。Stream流简化了集合与数组的操作,提升了代码的简洁性
84 11
Java——Stream流详解
|
2月前
|
Java 大数据 API
Java 流(Stream)、文件(File)和IO的区别
Java中的流(Stream)、文件(File)和输入/输出(I/O)是处理数据的关键概念。`File`类用于基本文件操作,如创建、删除和检查文件;流则提供了数据读写的抽象机制,适用于文件、内存和网络等多种数据源;I/O涵盖更广泛的输入输出操作,包括文件I/O、网络通信等,并支持异常处理和缓冲等功能。实际开发中,这三者常结合使用,以实现高效的数据处理。例如,`File`用于管理文件路径,`Stream`用于读写数据,I/O则处理复杂的输入输出需求。
|
6月前
|
Java
【JavaSE】Stream流
【JavaSE】Stream流
32 0
|
缓存 安全 Java
Java Stream 流详解
Java Stream(流)是Java 8引入的一个强大的新特性,用于处理集合数据。它提供了一种更简洁、更灵活的方式来操作数据,可以大大提高代码的可读性和可维护性。本文将详细介绍Java Stream流的概念、用法和一些常见操作。
345 0
|
Java API
JavaSE之 I/O流(一) stream
JavaSE之 I/O流(一) stream
69 0
|
Java API
JavaSE之 I/O流(一) File
JavaSE之 I/O流(一) File
49 0
|
API
java--Stream流
java--学习笔记
75 0
|
Java 大数据 数据处理
java的stream流
java的stream流
86 0
|
SQL Java 数据处理
java的Stream流
Java 8 引入的 Stream 流是一个非常强大且有用的概念,它提供了一种函数式编程的方式来处理集合数据。Stream 可以让我们以更简洁、更可读的方式进行数据处理和操作,极大地提高了代码的表达力和开发效率。本文将详细介绍 Java 的 Stream 流,包括其概念、特点和常用的操作方法等。
91 0