Java Stream API 有以下重要的方法:
Java Stream 是一系列函数或操作的流水线。这些操作可以分为两种类型:
- Intermediate Operation(中间操作)
- Terminal Operation(终止操作)
两者之间的区别在于操作创建的输出。如果一个操作输出另一个流,你可以对其应用进一步的操作,我们称之为中间操作。然而,如果操作输出具体类型或产生副作用,则为终止类型。
显然,终止操作后面不能跟随后续流操作,因为终止操作不返回流!
中间操作
中间操作总是懒执行。也就是说,它们不会运行,直到达到终止操作的点。我们将深入了解流中使用的几个最受欢迎的中间操作。
- map — map 操作返回经过传递给该操作的函数处理后的元素流。映射之前和之后的元素可能具有不同的类型,但元素的总数将保持不变。
- filter — filter 操作返回满足传递给该操作的谓词的元素流。在过滤之前和之后,元素本身将具有相同的类型,但元素的数量可能会改变。
- distinct — distinct 操作是 filter 操作的特例。基于元素的 equals 方法,distinct 返回一个流,使得流中的每个元素都是唯一的。
下面是一个总结表格,包括一些其他常见的中间操作。
终止操作
终止操作总是急切执行。该操作将启动流中存在的所有先前的惰性操作的执行。终止操作要么返回具体类型,要么产生副作用。例如,调用 Integer::sum 操作的 reduce 操作将生成一个 Optional,它是一个具体类型。另外,forEach 操作不返回具体类型,但你可以添加一个副作用,例如打印每个元素。collect 终止操作是 reduce 的一种特殊类型,它获取流中的所有元素,并可以生成一个 Set、Map 或 List。下面是一个表格总结。
流的示例
练习1:从字符串数字列表中获取偶数。
List<String> numbers = Arrays.asList("1", "2", "3", "4", "5", "6");//Convert list of string to even number listList<Integer> even = numbers.stream()
.map(s -> Integer.valueOf(s))
.filter(number -> number % 2 == 0)
.collect(Collectors.toList());
练习2:获取50岁或以上的前15个书籍作者的大写姓氏的唯一值。
List<String> surname =
library.stream()
.map(book -> book.getAuthor())
.filter(author -> author.getAge() >= 50)
.map(Author::getSurname)
.map(String::toUpperCase)
.distinct()
.limit(15)
.collect(toList()));
我们的流源 library
是一个 ArrayList。查看代码并跟随说明。从这个书籍列表中,我们首先需要从书籍到书籍作者的映射,这给我们一个 Authors 流,然后将它们过滤为只获取那些50岁或以上的作者。我们将 Author 的姓映射为 Strings,这将给我们一个 Strings 流。我们将其映射为大写的 Strings,并确保流中的元素是唯一的,并获取前15个。最后,我们使用 java.util.streams.Collectors
中的 toList 将其作为列表返回。
练习3:打印出所有年龄小于25岁的女性作者的年龄总和。
int sum = library.stream()
.map(Book::getAuthor)
.filter(author -> author.getGender() == Gender.FEMALE)
.map(Author::getAge)
.filter(age -> age < 25)
.reduce(0, Integer::sum)
使用相同的原始流,我们再次将元素从 Books 映射到 Authors,并仅对女性作者进行过滤。接下来,我们将元素从 Authors 映射到作者年龄,这给我们一个 int 流。我们将年龄过滤为小于25岁的年龄,并使用 reduce 操作和 Integer::sum 来计算年龄总和。
练习4:返回第一个薪资大于100000的员工。如果不存在这样的员工,则返回 null。
Employee employee =
Stream.of(empIds)
.map(employeeRepository::findById)
.filter(e -> e != null)
.filter(e -> e.getSalary() > 100000)
.findFirst()
.orElse(null);
练习4:将流转换为数组。
Employee[] employees = empList.stream().toArray(Employee[]::new);
包装和解包
有时,我们需要将基本值转换为其包装器等效项。
在这些情况下,我们可以使用 boxed() 方法:
List<Integer> evenInts = IntStream.rangeClosed(1, 10)
.filter(i -> i % 2 == 0)
.boxed()
.collect(Collectors.toList());
我们还可以从包装类流转换为基本流:
// returns 78
int sum = Arrays.asList(33,45)
.stream()
.mapToInt(i -> i)
.sum();
我们总是可以使用 mapToXxx 和 flatMapToXxx 方法创建基本流。
Java Stream 的创建
让我们首先从现有数组中获取流:
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();
请注意,Java 8 添加了一个新的 stream() 方法到 Collection 接口。
我们还可以使用 Stream.of() 从单个对象创建流:
Stream.of(arrayOfEmps[0], arrayOfEmps[1], arrayOfEmps[2]);
或者简单地使用 Stream.builder():
Stream.Builder<Employee> empStreamBuilder = Stream.builder();empStreamBuilder.accept(arrayOfEmps[0]);
empStreamBuilder.accept(arrayOfEmps[1]);
empStreamBuilder.accept(arrayOfEmps[2]);Stream<Employee> empStream = empStreamBuilder.build();
评论(0)