没有isFinite()和isOrdered()方法,如何安全地使用Java Streams?

时间:2019-06-10 15:59:09

标签: java java-stream

存在一个关于java方法是否应该返回Collections or Streams的问题,在该问题中Brian Goetz回答说,即使对于有限序列,通常也应该首选Streams。

但是在我看来,目前无法安全地执行来自其他地方的Streams上的许多操作,并且防御性的代码保护措施是不可能的,因为Streams不会显示它们是无限的还是无序的。

如果要在Stream()上执行的操作遇到并行问题,我可以调用isParallel()进行检查或顺序执行,以确保计算是并行的(如果我记得的话)。

但是,如果有序性或无穷大(大小)与我的程序的安全性有关,则我无法编写保护措施。

假设我使用了一个实现此虚拟接口的库:

public interface CoordinateServer {
    public Stream<Integer> coordinates();
    // example implementations:
    // IntStream.range(0, 100).boxed()   // finite, ordered, sequential
    // final AtomicInteger atomic = new AtomicInteger();
    // Stream.generate(() -> atomic2.incrementAndGet()) // infinite, unordered, sequential
    // Stream.generate(() -> atomic2.incrementAndGet()).parallel() // infinite, unordered, parallel
}

那我可以安全地对此流调用哪些操作以编写正确的算法?

看来,如果我想将元素写入文件是一种副作用,我需要担心流是并行的:

// 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?

并且如果是并行的,则基于并行的Threadpool?

如果我想对流进行排序(或其他非短路操作),则在某种程度上需要谨慎对待它的无限性:

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的宝贵属性。

此外,Streams may sometimes need to be closed,但最常见的不是。如果我使用某个方法的流(来自某个方法参数的),通常应该调用close吗?

另外,流可能已经被消耗掉了,能够优雅地处理这种情况将是一件好事,因此check if the stream has already been consumed;

我希望有一些代码片段,可用于在处理之前验证有关流的假设,例如>

Stream<X> stream = fooLibrary.getStream();
Stream<X> safeStream = StreamPreconditions(
    stream, 
    /*maxThreshold or elements before IllegalArgumentException*/
    10_000,
    /* fail with IllegalArgumentException if not ordered */
    true
    )

1 个答案:

答案 0 :(得分:2)

据我所知(经过一些实验和here)后,还无法确定流是否是有限的。

除此之外,有时甚至在运行时也无法确定(例如在Java 11-chainID中)。

您可以做的是:

  1. 您可以通过几种方式确定它是否是有限的(请注意,在这些方法上接收到false并不意味着它是无限的,只是可能是无限的):

    1. chainID teamID statID startType endType 2 Team B Entry TO SH 2 Team B Shot TO SH 3 Team A Entry ST TO 3 Team A Ineffective Pass ST TO 4 Team B Effective Pass TO ST 4 Team B Effective Pass TO ST 4 Team B Ineffective Pass TO ST 5 Team A Entry TO SH 5 Team A Goal TO SH -如果已知的确切大小是有限的,否则它将返回-1。

    2. IntStream.generate(() -> 1).takeWhile(x -> externalCondition(x))-如果为stream.spliterator().getExactSizeIfKnown(),则返回true。

  2. 您可以假设最坏的情况(取决于您的情况)来保护自己。

    1. stream.spliterator().hasCharacteristics(Spliterator.SIZED)-明确设置您的首选消费类型。
    2. 对于潜在的无限流,假设每种情况下的最坏情况。

      1. 例如,假设您想听一连串推文,直到通过Venkat找到一条推文为止-这可能是无限的操作,但是您要等到找到这样一条推文。因此,在这种情况下,只需进行SIZED-它会反复进行,直到出现此类推文为止(或永远如此)。
      2. 另一种情况,可能是更常见的情况,是想对所有元素执行某些操作,或者只想尝试一定时间(类似于超时)。为此,我建议始终在调用操作(stream.sequential()/stream.parallel()stream.filter(tweet -> isByVenkat(tweet)).findAny()或类似操作)之前先调用stream.limit(x),其中collect是您愿意接受的尝试次数。

毕竟,我只想提一下,我认为返回流通常不是一个好主意,除非有很大的好处,否则我会尽量避免使用它。