Java 新增的Stream方法,用户希望使用 Java 9 为 Stream
接口添加的新特性,使用 Stream
接口新增的 ofNullable
、iterate
、takeWhile
以及 dropWhile
方法。Java 9 为 Stream
接口引入了 ofNullable
、iterate
、takeWhile
、dropWhile
等新方法,本范例将讨论它们的用法。
Java 新增的Stream方法 问题描述
用户希望使用 Java 9 为 Stream
接口添加的新特性。
Java 新增的Stream方法 解决方案
使用 Stream
接口新增的 ofNullable
、iterate
、takeWhile
以及 dropWhile
方法。
Java 新增的Stream方法 具体实例
Java 9 为 Stream
接口引入了 ofNullable
、iterate
、takeWhile
、dropWhile
等新方法,本范例将讨论它们的用法。
1.ofNullable
方法
在 Java 8 中,of
方法包括两种形式,一种传入单个值,另一种传入可变参数列表。无论哪种形式,参数都不能为空。
而在 Java 9 中,新的 ofNullable
方法可以在参数不为空时返回一个包装值的单元素流,为空时返回一个空流。例 10-16 的用例显示了该方法的应用。
例 10-16
ofNullable
方法的应用
@Test
public void ofNullable() throws Exception {
Stream<String> stream = Stream.ofNullable("abc"); ➊
assertEquals(1, stream.count());
stream = Stream.ofNullable(null); ➋
assertEquals(0, stream.count());
}
❶ 单元素流
❷ 返回 Stream.empty()
在本例中,count
方法返回流中非空元素的数量,我们可以借此在任何参数上使用 ofNullable
方法,而无须首先检查参数是否为空。
2.传入Predicate
的iterate
方法
另一种有趣的方法是 iterate
。在 Java 8 中,iterate
方法的签名如下:
static<T> Stream<T> iterate(final T seed, final UnaryOperator<T> f)
创建流时,从初始元素种子开始,对种子依次应用一元运算符以产生后续元素。由于生成的流是一个无限流,通常需要采用 limit
或其他短路操作(short-circuiting operation,如 findFirst
或 findAny
)来控制返回流的大小。
在 Java 9 中,iterate
方法新增了一种重载形式,它传入 Predicate
作为第二个参数:
static<T> Stream<T> iterate(T seed, Predicate<? super T> hasNext,
UnaryOperator<T> next)
创建流时,从初始元素种子开始,对种子应用一元运算符,直至值不再满足谓词 hasNext
。
相关应用如例 10-17 所示。
例 10-17 传入
Predicate
的iterate
方法
@Test
public void iterate() throws Exception {
List<BigDecimal> bigDecimals = ➊
Stream.iterate(BigDecimal.ZERO, bd -> bd.add(BigDecimal.ONE))
.limit(10)
.collect(Collectors.toList());
assertEquals(10, bigDecimals.size());
bigDecimals = Stream.iterate(BigDecimal.ZERO, ➋
bd -> bd.longValue() < 10L,
bd -> bd.add(BigDecimal.ONE))
.collect(Collectors.toList());
assertEquals(10, bigDecimals.size());
}
❶ 创建 BigDecimal
流(Java 8 实现)
❷ 创建 BigDecimal
流(Java 9 实现)
第一个流使用 iterate
方法,并通过 limit
方法控制大小,这是 Java 8 的实现方式;第二个流传入 Predicate
作为第二个参数,看起来更像是传统的 for
循环。
3.takeWhile
与dropWhile
方法
Java 9 新增了 takeWhile
和 dropWhile
方法,二者基于谓词获取流的某一部分。根据 Javadoc 的描述,对于有序流,takeWhile
方法从流的起始位置开始,返回“匹配给定谓词的元素的最长前缀”。
dropWhile
方法的作用正好相反,在丢弃匹配给定谓词的元素的最长前缀后,该方法将返回流的其余元素。
两种方法在有序流上的应用如例 10-18 所示。
例 10-18 获取与丢弃流中的元素
@Test
public void takeWhile() throws Exception {
List<String> strings = Stream.of("this is a list of strings".split(" "))
.takeWhile(s -> !s.equals("of")) ➊
.collect(Collectors.toList());
List<String> correct = Arrays.asList("this", "is", "a", "list");
assertEquals(correct, strings);
}
@Test
public void dropWhile() throws Exception {
List<String> strings = Stream.of("this is a list of strings".split(" "))
.dropWhile(s -> !s.equals("of")) ➋
.collect(Collectors.toList());
List<String> correct = Arrays.asList("of", "strings");
assertEquals(correct, strings);
}
❶ 当不再满足谓词时,返回谓词之前的元素
❷ 当不再满足谓词时,返回谓词之后的元素
可以看到,两种方法在同一个位置将流拆分,不过 takeWhile
返回拆分位置之前的元素,而 dropWhile
返回拆分位置之后的元素。
takeWhile
方法的最大优点在于它是一种短路操作:对于一个包含大量排序元素的集合,只要达到所设定的条件,就可以停止求值。
例如,假设存在一个由客户订单构成的集合,集合中的元素以降序方式排序。借由 takeWhile
方法,我们可以只获取高于某个阈值的订单,而不必筛选每个元素。
例 10-19 模拟了这种情况。程序生成 50 个 0 到 100 之间的随机整数,对它们做降序排序,然后仅返回大于 90 的整数。
例 10-19 对整数流应用
takeWhile
方法
Random random = new Random();
List<Integer> nums = random.ints(50, 0, 100) ➊
.boxed() ➋
.sorted(Comparator.reverseOrder())
.takeWhile(n -> n > 70) ➌
.collect(Collectors.toList());
❶ 生成 50 个 0 到 100 之间的随机整数
❷ 将这些整数装箱,以便采用 Comparator
排序并收集
❸ 将流拆分并返回大于 90 的整数
改用 dropWhile
方法或许能让本例看起来更为直观(尽管效率未必会提高),如例 10-20 所示。
例 10-20 对整数流应用
dropWhile
方法
Random random = new Random();
List<Integer> nums = random.ints(50, 0, 100)
.sorted() ➊
.dropWhile(n -> n < 90) ➋
.boxed()
.collect(Collectors.toList());
❶ 升序排序
❷ 将流拆分并返回大于 90 的整数
类似 takeWhile
和 dropWhile
这样的方法在其他语言中已存在多年,Java 9 将二者正式引入 Java。