java - 有条件地向 Java 8 流添加操作

标签 java java-8 limit java-stream

我想知道是否可以根据在流之外设置的某种条件向流添加操作。例如,如果我的 limit,我想对流添加限制操作变量不等于 -1 .

我的代码目前看起来像这样,但我还没有看到以这种方式使用流的其他示例,其中将 Stream 对象重新分配给应用于自身的中间操作的结果:

// Do some stream stuff
stream = stream.filter(e -> e.getTimestamp() < max);

// Limit the stream
if (limit != -1) {
   stream = stream.limit(limit);
}

// Collect stream to list
stream.collect(Collectors.toList());

如本文所述 stackoverflow post ,在调用终端操作之前实际上不会应用过滤器。由于我在调用终端操作之前重新分配了流的值,上面的代码仍然是使用 Java 8 流的正确方法吗?

最佳答案

链式调用系列和存储中间返回值的系列调用之间没有语义差异。因此,以下代码片段是等效的:

a = object.foo();
b = a.bar();
c = b.baz();


c = object.foo().bar().baz();

在任何一种情况下,每个方法都是根据前一次调用的结果调用的。但在后一种情况下,中间结果不会被存储,而是会在下一次调用时丢失。在流 API 的情况下,中间结果不得在调用 next 方法后使用,因此链接是使用流的自然方式,因为它本质上确保您不会在一个方法上调用多个方法返回引用。

尽管如此,只要遵守不多次使用返回引用的约定,存储对流的引用并没有错。通过像在您的问题中那样使用它,即用下一次调用的结果覆盖变量,您还可以确保不会对返回的引用调用多个方法,因此,这是正确的用法。当然,这只适用于相同类型的中间结果,所以当你使用 map 时或 flatMap ,获取不同引用类型的流,您不能覆盖局部变量。那么你要注意不要再次使用旧的局部变量,但是,只要你在下次调用后不使用它,中间存储就没有问题。

有时,您必须存储它,例如
try(Stream<String> stream = Files.lines(Paths.get("myFile.txt"))) {
    stream.filter(s -> !s.isEmpty()).forEach(System.out::println);
}

请注意,该代码等效于以下替代方案:
try(Stream<String> stream = Files.lines(Paths.get("myFile.txt")).filter(s->!s.isEmpty())) {
    stream.forEach(System.out::println);
}


try(Stream<String> srcStream = Files.lines(Paths.get("myFile.txt"))) {
    Stream<String> tmp = srcStream.filter(s -> !s.isEmpty());
    // must not be use variable srcStream here:
    tmp.forEach(System.out::println);
}

它们是等价的,因为 forEach总是在 filter 的结果上调用它总是在 Files.lines 的结果上被调用最终的结果close()无关紧要当关闭影响整个流管道时调用操作。

一言以蔽之,你用​​它的方式,是对的。

我什至更喜欢这样做,因为不链接 limit当您不想应用限制时,操作是表达您意图的最干净方式。还值得注意的是,建议的替代方案可能在很多情况下都有效,但它们在语义上并不等效:
.limit(condition? aLimit: Long.MAX_VALUE)

假设您可能遇到的最大元素数是 Long.MAX_VALUE但是流可以有更多的元素,它们甚至可能是无限的。
.limit(condition? aLimit: list.size())

当流源为 list , 打破了流的惰性求值。原则上,可变流源可以合法地任意更改直到终端操作开始时为止。结果将反射(reflect)到目前为止所做的所有修改。当您添加包含 list.size() 的中间操作时,即此时列表的实际大小,应用于此点和终端操作之间的集合的后续修改可能会使此值具有与预期的“实际上没有限制”语义不同的含义。

比较 “Non Interference” section of the API documentation :

For well-behaved stream sources, the source can be modified before the terminal operation commences and those modifications will be reflected in the covered elements. For example, consider the following code:

List<String> l = new ArrayList(Arrays.asList("one", "two"));
Stream<String> sl = l.stream();
l.add("three");
String s = sl.collect(joining(" "));

First a list is created consisting of two strings: "one"; and "two". Then a stream is created from that list. Next the list is modified by adding a third string: "three". Finally the elements of the stream are collected and joined together. Since the list was modified before the terminal collect operation commenced the result will be a string of "one two three".



当然,这是一种罕见的极端情况,因为程序员通常会在不修改其间的源集合的情况下制定整个流管道。尽管如此,不同的语义仍然存在,一旦进入这样的极端情况,它可能会变成一个很难找到的错误。

此外,由于它们不是等价的,因此流 API 永远不会将这些值识别为“实际上没有限制”。甚至指定 Long.MAX_VALUE意味着流实现必须跟踪已处理元素的数量,以确保遵守限制。因此,不添加 limit与添加一个程序员期望永远不会超过的数字的限制相比,操作可以具有显着的性能优势。

关于java - 有条件地向 Java 8 流添加操作,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33746357/

相关文章:

php - 如何随机给mysql查询限制

java - Android布局文件不允许&、<、>等字符

java - 找不到标志…?

java - java中如何输入小数

java - 每当我尝试重建项目时,它都会出现编译错误,无法在 android studio 中完成

Java 8 : How to get particular value within object inside list without converting stream back to list and fetch value at 0th location?

java-8 - RxJava 文件和运算符链接

mysql - RODBC 更改 group_concat 的限制

java - 如何模拟 object.getMap().get ("String_Key_to_return_Object")

c++ - 在 Ubuntu 64 位操作系统中无法读取大于 2GB 的文件