问题是Java方法是否应该返回 Collections还是Streams,Brian Goetz回答说,即使对于有限序列,通常应该首选Streams。
但是在我看来,当前从其他地方获取的Streams上许多操作无法安全执行,并且防御性代码保护不可能,因为Streams不会透露它们是否无限或无序。
如果并行处理对我想要执行的Stream操作造成问题,我可以调用isParallel()进行检查,或者使用sequential确保计算是串行的(如果我记得的话)。
但是,如果有序性或有限性(大小)与程序的安全性相关,则无法编写保护措施。
假设我使用实现此虚构接口的库:
public interface CoordinateServer {
public Stream<Integer> coordinates();
// example implementations:
// finite, ordered, sequential
// IntStream.range(0, 100).boxed()
// final AtomicInteger atomic = new AtomicInteger();
// // infinite, unordered, sequential
// Stream.generate(() -> atomic2.incrementAndGet())
// infinite, unordered, parallel
// Stream.generate(() -> atomic2.incrementAndGet()).parallel()
// finite, ordered, sequential, should-be-closed
// Files.lines(Path.path("coordinates.txt")).map(Integer::parseInt)
}
那么我可以在这个流上进行哪些操作,以编写正确的算法?
如果我希望将元素作为副作用写入文件,则似乎需要关注流是否并行:
// if stream is parallel, which order will be written to file?
coordinates().peek(i -> {writeToFile(i)}).count();
// how should I remember to always add sequential() in such cases?
而且如果它是并行的,那么基于什么线程池使其并行?
如果我想对流进行排序(或其他非短路操作),我需要注意它是否为无限流:
coordinates().sorted().limit(1000).collect(toList()); // will this terminate?
coordinates().allMatch(x -> x > 0); // will this terminate?
在排序之前,我可以设置一个限制,但是如果我期望一个未知大小的有限流,应该设置什么神奇的数字呢?
最后,也许我想要并行计算以节省时间,然后收集结果:
// will result list maintain the same order as sequential?
coordinates().map(i -> complexLookup(i)).parallel().collect(toList());
但如果流没有被排序(在该库的版本中),那么由于并行处理,结果可能会变得混乱。但我该如何防范这种情况,除了不使用并行(这将破坏性能目的)之外?
集合明确了它们是有限的还是无限的,是否有顺序,并且它们不携带处理模式或线程池等信息。这些似乎是API的有价值属性。
此外,流有时需要关闭,但大多数情况下并非如此。如果我从一个方法中消费流(或者从一个方法参数中消费),我通常应该调用close吗?
此外,流可能已经被消费,因此最好能够优雅地处理这种情况,所以最好检查流是否已经被消费;
我希望有一些代码片段可以用来验证关于流的假设,在处理流之前,像:
Stream<X> stream = fooLibrary.getStream();
Stream<X> safeStream = StreamPreconditions(
stream,
/*maxThreshold or elements before IllegalArgumentException*/
10_000,
/* fail with IllegalArgumentException if not ordered */
true
)