当前位置: 首页 > 知识库问答 >
问题:

Java8流:计算进入终端操作的所有元素

邹锦
2023-03-14

我想知道是否有更好的(或只是其他)方法来获得进入流的终端操作的所有项目的计数,而不是以下方法:

Stream<T> stream = ... // given as parameter
AtomicLong count = new AtomicLong();
stream.filter(...).map(...)
      .peek(t -> count.incrementAndGet())

其中count.get()给出了该阶段处理项目的实际计数。

我故意跳过了终端操作,因为这可能会在<代码>之间发生变化。forEach,。减少或。收集。我确实知道。已经开始计数了,但只有我交换了一个,它才能正常工作。forEach带有一个。映射并使用。将计为终端操作。但在我看来,这似乎是<代码>。然后地图被误用。

我真正不喜欢上述解决方案的地方是:如果在其后面添加一个过滤器,它只计算该特定阶段的元素,而不是将进入终端操作的元素。

我想到的另一种方法是将过滤和映射的值收集到一个列表中并对其进行操作,然后调用list.size()来获取计数。然而,如果流的收集会导致错误,这将不起作用,而使用上述解决方案,如果适当的try/catch到位,我可以对迄今为止所有处理的项目进行计数。然而,这不是硬性要求。


共有2个答案

竺辉
2023-03-14

最好的想法是对自身使用映射,同时计算映射例程的调用。

steam.map(object -> {counter.incrementAndGet(); return object;});

由于此lambda可以重用,并且可以用对象替换任何lambda,因此可以创建如下计数器对象:

class StreamCounter<T> implements Function<? super T,? extends T> {
  int counter = 0;
  public T apply(T object) { counter++; return object;}
  public int get() { return counter;}
}

所以使用:

StreamCounter<String> myCounter = new ...;
stream.map(myCounter)...
int count = myCounter.get();

由于映射调用只是另一个重用点,因此可以通过扩展流和包装普通流来提供映射方法。

通过这种方式,您可以创建以下内容:

AtomicLong myValue = new AtomicLong();
...
convert(stream).measure(myValue).map(...).measure(mySecondValue).filter(...).measure(myThirdValue).toList(...);

通过这种方式,您可以简单地拥有自己的Stream包装器,该包装器以自己的版本透明地包装每个流(没有性能或内存开销)并测量任何此类度量点的基数。

这通常在创建映射/约简解决方案时分析算法的复杂性时完成。扩展流实现,不使用原子长的实例进行计数,而只使用测量点的名称。流实现可以容纳无限数量的测量点,同时提供了一种灵活的打印报告的方法。

这种实现可以记住流方法的具体序列以及每个测量点的位置,并产生如下输出:

list ->  (32k)map -> (32k)filter -> (5k)map -> avg(). 

这样的流实现只编写一次,可以用于测试,也可以用于报告。

内置到每日实现中,可以为某些处理收集统计信息,并允许通过使用不同的操作排列进行动态优化。例如,这将是一个查询优化器。

因此,在您的情况下,最好的方法是首先重用流计数器,并根据使用频率、计数器数量和对DRY原理的亲和力最终在以后实现更复杂的解决方案。

PS:StreamCounter使用int值,并且不是线程安全的,因此在并行流设置中,可以用原子整数实例替换int。

宰烈
2023-03-14

在终端操作IMO之前,您似乎已经通过peek获得了最干净的解决方案。我认为这是出于调试目的而需要的唯一原因-如果是这样的话,那么peek就是为此而设计的。为此包装流并提供单独的实现实在太多了——除了大量的时间和以后对添加到流中的所有内容的支持之外。

如果添加了另一个过滤器怎么办?好吧,提供一个代码注释(我们很多人都这样做)和一些否则会失败的测试用例。

只是我的0.02美元

 类似资料:
  • 给定两个序列和,长度相同。在每个步骤中,您可以设置if

  • 我正在尝试使用流将一个列表映射到另一个列表。 原始列表的某些元素无法映射。也就是说,映射函数可能无法找到合适的新值。 null 对更好的方法的建议?或者我应该把溪流全部挖开,用好的旧循环?

  • 我读了IntStream::NoneMatch. 的Javadoc 返回此流中是否没有元素与提供的谓词匹配。如果不是确定结果所必需的,则不能计算所有元素上的谓词。如果流为空,则返回true并且不计算谓词。  我想知道是否存在一种实际情况,其中不会对流的所有元素求值谓词并返回true(在返回false的情况下,很明显,只有在找到第一个匹配项之前,才对元素求值谓词)。 我能想到的唯一情况是,如果流管道

  • 我试图了解是否有一种方法可以在不检查整个流程的情况下终止减少操作,我无法找到一种方法。 用例大致如下:让有一长串需要折叠成的s。每个元素检查都可能很昂贵,因此在中,我对传入的执行检查,看看我们是否需要执行昂贵的操作-如果我们不需要,那么我只需返回累加器。 这对于小列表(er)显然是一个很好的解决方案,但大列表会产生不必要的流元素访问成本,我希望避免这种情况。 这里是一个代码草图-仅假设序列缩减。

  • 我正在使用一个3d party库,它们返回缺少类型规范的集合(例如,

  • 问题内容: 考虑以下代码: 终端操作(如)是否关闭已打开的基础文件? 请参阅Files.list的javadoc的相关部分: 返回的流封装了DirectoryStream。如果需要及时处理文件系统资源,则应使用try-with- resources构造来确保在流操作完成之后调用流的close方法。 如果不调用,那么在生成可维护代码时最好的替代方法是什么? 问题答案: 终端操作员不会自动关闭流。考虑