简体   繁体   English

Java stream 中的计数和打印值

[英]Counting AND printing values in Java stream

I am writing a code, that runs a collection through a stream, and in the stream it filters the data with several filter functions, and then prints the result of filtering:我正在编写一个代码,它通过 stream 运行集合,在 stream 中,它使用几个过滤器函数过滤数据,然后打印过滤结果:

List<String> someList = Arrays.asList("string1", "string2", etc.);

someList.stream().filter(s -> predicate1()).filter(s -> predicate2()).forEach(System.out::println);

and what would greatly help me, would be the ability to print and count filtered elements.对我有很大帮助的是打印和计算过滤元素的能力。 Since forEach() uses consumer function, and returns Void type, I cannot do anything further to the data.由于forEach()使用消费者 function,并返回 Void 类型,我无法对数据做任何进一步的事情。 But if I change forEach() with count() , I receive a long value, and stream is ended this way or another.但是,如果我将forEach()更改为count() ,我会收到一个 long 值,并且 stream 会以这种或其他方式结束。 Is there any elegant way to have something like:有没有什么优雅的方式来拥有类似的东西:

long counted = someList.stream().filter(s -> predicate1()).filter(s -> predicate2()).map??reduce??(printAndAlsoCountPrintedAndReturnFinalCountValue())

in one stream without having to run it 2 times, first with count() , and the second with forEach(System.out::println) ?在一个 stream 中,无需运行两次,第一次使用count() ,第二次使用forEach(System.out::println) I was thinking about some mapper function, but map() also returns Stream data, so even if I did some mapping printing-counting function, I don't see how I'd have to return counted value into a parameter.我在考虑一些映射器 function,但是map()也返回 Stream 数据,所以即使我做了一些映射打印计数 ZC1C425268E68385D1AB5074C17A94F14,我也没有看到如何返回。

Generally it's not a problem to run the collection through 2 streams, but I believe it's not very resource-wise, depending on how heavy the filtering functions would be, that's why I am looking for more "clever" solution, if it exists.通常,通过 2 个流运行集合不是问题,但我认为这不是很节省资源,这取决于过滤功能的繁重程度,这就是为什么我正在寻找更“聪明”的解决方案(如果存在)。

Thank you in advance.先感谢您。

You can use reduce function:您可以使用reduce function:


import java.util.Arrays;
class HelloWorld {
    public static void main(String[] args) {
        var l = Arrays.asList(1,2,3,4,5,6,7,8);
        var count= l.stream()
                    .filter(x -> x%2 == 0)
                    .reduce(0, (acc, elem) -> {
                        System.out.println(elem);
                        return acc + 1; 
                    });
        System.out.println("Total Count: " + count);
    }
}

This will print “Total Count: 4” as well as the elements themselves (2,4,6,8)这将打印“Total Count: 4”以及元素本身 (2,4,6,8)

Note that this printing is a side effect so personally I would also consider writing this “as usual” with for-loops (in an imperative style) without streams at all.请注意,这种打印是一种副作用,因此我个人也会考虑使用 for-loops(以命令式风格)“照常”编写,根本没有流。

The problem as it stated now is contradiction with the Single responsibility principle , which the first principle of SOLID.它现在所说的问题与 SOLID 的第一原则单一责任原则相矛盾。

If you aim to write clean a maintainable code, you can generate a collection as a result.如果您的目标是编写干净的可维护代码,则可以生成一个集合作为结果。 And then you can do whatever you need with its contents and find out its size as well.然后你可以对它的内容做任何你需要的事情,并找出它的大小。

public List<String> foo(List<String> source) {
    
    return source.stream()
        .filter(s -> predicate1())
        .filter(s -> predicate2())
        .toList(); // for Java 16+ or collect(Collectors.toList());
}

If you need to see all the elements that passed all the filters in the pipeline on the console, you can use peek() .如果您需要在控制台上查看通过管道中所有过滤器的所有元素,可以使用peek() This method was introduces in the Stream API for debugging.该方法在Stream API中引入,用于调试。

long counted = someList.stream()
    .filter(s -> predicate1())
    .filter(s -> predicate2())
    .peek(System.out::println)
    .count();

Note: that this method should be used only for debugging purposes.注意:此方法应用于调试目的。 If let's say instead of printing you would decide to store these elements into a collection, then you need to be aware that it's discouraged by API the documentation .如果假设您决定将这些元素存储到集合中而不是打印,那么您需要注意API 文档不鼓励这样做。

And, also, pay attention to the documentation of peek()而且,还要注意peek()的文档

This method exists mainly to support debugging , where you want to see the elements as they flow past a certain point in a pipeline此方法的存在主要是为了支持调试,您希望在元素流过管道中的某个点时查看它们。

In cases where the stream implementation is able to optimize away the production of some or all the elements (such as with short-circuiting operations like findFirst , or in the example described in count() ), the action will not be invoked for those elements .如果 stream 实现能够优化部分或所有元素的生成(例如使用findFirst之类的短路操作,或在count()中描述的示例中),则不会为这些元素调用操作.

That means when have in mind something more important, than printing elements on the console, then you have to stick with the first approach.这意味着当考虑到比在控制台上打印元素更重要的事情时,您必须坚持第一种方法。

It's not really intended to be used for that, but you could use an AtomicLong .它并不是真的打算用于此,但您可以使用AtomicLong

    List<String> someList  = Arrays.asList("string1", "string2", "s1", "s2");
    AtomicLong count = new AtomicLong();
    someList .stream()
            .filter(s -> s.contains("string"))
            .forEach(s -> {
                System.out.println(s);
                count.incrementAndGet();
            });
    System.out.println(count.get());

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM