昨天总结了一下stream中的一些方法的基础使用,现在做一些其他的总结
正如我们一直在讨论的,Java 流操作分为中间操作和终端操作。
诸如 filter() 之类的中间操作会返回一个可以在其上进行进一步处理的新流。诸如 forEach() 之类的终端操作将流标记为已使用,之后就不能再进一步使用它。
流管道由流源、零个或多个中间操作和终端操作组成。
这是一个示例流管线,其中Emplist是源,Filter()是中间操作,计数是终端操作:
@Test
public void whenStreamCount_thenGetElementCount() {
Long empCount = empList.stream()
.filter(e -> e.getSalary() > 200000)
.count();
assertEquals(empCount, new Long(1));
}
一些操作被认为是短路操作。短路操作允许对无限流的计算在有限时间内完成:
@Test
public void whenLimitInfiniteStream_thenGetFiniteElements() {
Stream<Integer> infiniteStream = Stream.iterate(2, i -> i * 2);
List<Integer> collect = infiniteStream
.skip(3)
.limit(5)
.collect(Collectors.toList());
assertEquals(collect, Arrays.asList(16, 32, 64, 128, 256));
}
在这里,我们使用短路操作Skip()跳过前3个元素,并限制()限制使用使用Iterate()生成的无限流中的5个元素。
稍后,我们将更多地谈论无限流。
Java 流最重要的特性之一是它们允许通过惰性求值进行显着优化。
仅在启动终端操作时才对源数据进行计算,并且仅在需要时消耗源元素。
所有中间操作都是惰性的,因此在实际需要处理结果之前不会执行它们。
例如,考虑我们之前看到的 findFirst() 示例。 map() 操作在这里执行了多少次? 4 次,因为输入数组包含 4 个元素?
@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));
}
Stream 执行映射和两个过滤操作,一次一个元素。
它首先对 id 1 执行所有操作。由于 id 1 的薪水不大于 100000,因此处理转到下一个元素。
Id 2 满足两个过滤器谓词,因此流计算终端操作 findFirst() 并返回结果。
没有对 id 3 和 4 执行任何操作。
延迟处理流可以避免在不必要时检查所有数据。当输入流是无限的而不仅仅是非常大时,这种行为变得更加重要。
让我们从 sorted() 操作开始——它根据我们传递给它的比较器对流元素进行排序。
例如,我们可以根据员工的姓名对员工进行排序:
@Test
public void whenSortStream_thenGetSortedStream() {
List<Employee> employees = empList.stream()
.sorted((e1, e2) -> e1.getName().compareTo(e2.getName()))
.collect(Collectors.toList());
assertEquals(employees.get(0).getName(), "Bill Gates");
assertEquals(employees.get(1).getName(), "Jeff Bezos");
assertEquals(employees.get(2).getName(), "Mark Zuckerberg");
}
请注意,短路不会用于排序()。
这意味着在上面的示例中,即使我们在sorted()之后使用了findfirst(),所有元素的排序也是在应用findfirst()之前完成的。发生这种情况是因为操作不知道第一个元素在整个流进行排序之前。
顾名思义,min() 和 max() 基于比较器分别返回流中的最小和最大元素。它们返回 Optional 因为结果可能存在也可能不存在(例如,由于过滤):
@Test
public void whenFindMin_thenGetMinElementFromStream() {
Employee firstEmp = empList.stream()
.min((e1, e2) -> e1.getId() - e2.getId())
.orElseThrow(NoSuchElementException::new);
assertEquals(firstEmp.getId(), new Integer(1));
}
我们也可以避免使用 Comparator.comparing() 来定义比较逻辑:
@Test
public void whenFindMax_thenGetMaxElementFromStream() {
Employee maxSalEmp = empList.stream()
.max(Comparator.comparing(Employee::getSalary))
.orElseThrow(NoSuchElementException::new);
assertEquals(maxSalEmp.getSalary(), new Double(300000.0));
}
distinct() 不接受任何参数并返回流中的不同元素,消除重复。它使用元素的 equals() 方法来判断两个元素是否相等:
@Test
public void whenApplyDistinct_thenRemoveDuplicatesFromStream() {
List<Integer> intList = Arrays.asList(2, 5, 3, 2, 4, 3);
List<Integer> distinctIntList = intList.stream().distinct().collect(Collectors.toList());
assertEquals(distinctIntList, Arrays.asList(2, 5, 3, 4));
}
这些操作都采用谓词并返回布尔值。应用短路并在确定答案后立即停止处理:
@Test
public void whenApplyMatch_thenReturnBoolean() {
List<Integer> intList = Arrays.asList(2, 4, 5, 6, 8);
boolean allEven = intList.stream().allMatch(i -> i % 2 == 0);
boolean oneEven = intList.stream().anyMatch(i -> i % 2 == 0);
boolean noneMultipleOfThree = intList.stream().noneMatch(i -> i % 3 == 0);
assertEquals(allEven, false);
assertEquals(oneEven, true);
assertEquals(noneMultipleOfThree, false);
}
AllMatch()检查流中所有元素是否为true。在这里,它一旦遇到5,它就会返回False,这是不可分解的2。
AnyMatch()检查流中任何一个元素是否为true true。在这里,再次应用短路,并在第一个元素之后立即返回TRUE。
nonematch()检查是否没有匹配谓词的元素。在这里,它只会在遇到6的6时立即返回错误,这是可以除以3的3。