繁体   English   中英

在其他任何中间方法之前,如何在Java 8流的开头运行单个函数?

[英]How can I run a single function on the opening of a Java 8 stream, before any other intermediate methods?

老实说,我什至不确定在相邻代码行之外这是否可行。 但是,我认为我会向社区询问。

Java 8允许通过Stream API管理其Collection。 在以前需要外部迭代的地方,现在我们可以简单地使用:

<Collection>.stream().forEach([expression]);

当我尝试快速格式化大型集合并将其输出到BufferedOutputStream或其他某个目标时,这非常好。 我需要做的是在我旁边写一些信息。 到目前为止,我能想到的最好的方法是:

labelData();
firstNames.stream().sequential().forEach(sz -> reportData(sz));

而如果我可以这样做,那将是非常不错的事情:

firstNames.stream().sequential().
    .onOpen(labelData())
    .forEach(sz->reportData(sz));

我知道可以使用BaseStream.onClose(Runnable closeHandler)设置打开-关闭方法。 是否存在这种开放方法?

谢谢你的时间。

这个怎么样:

labelData();
firstNames.forEach(this::reportData);

有时,简单的答案会更好。

这就是map()的作用。

firstNames.map(e -> addLabel(e)).forEach(this::reportData);

map()是一个中间操作,它在处理每个元素时对其进行转换。

例如,代码:

Stream.of("One","Two", "Three", "Four");
        .map(e -> "Number " + e)
        .forEach(System.out::println);

将输出:

Number One
Number Two
Number Three
Number Four

如果您真的只想在流之前写一些东西,那就这样做。 无论如何,在流中调用它的效率要低得多。 如果只需要在流包含元素的情况下将其写出,则可以执行以下操作:

package stream;

public class OneTimePrinter {
    private boolean shouldPrint = true;

    public void print(Object o){
        if (shouldPrint){
            System.out.println(o);
            shouldPrint = false;
        }
    }
}

然后在peek()操作中使用该OneTimePrinter类:

OneTimePrinter printer1 = new OneTimePrinter();
Stream.of("Five","Six", "Seven", "Eight")
.peek((e) -> printer1.print("TEST OUTPUT:\n------------"))
.map(e -> "Number " + e)
.forEach(System.out::println);  

OneTimePrinter printer2 = new OneTimePrinter();
Stream.of()
.peek((e) -> printer2.print("TEST OUTPUT:\n------------"))
.map(e -> "Number " + e)
.forEach(System.out::println);  

将输出:

TEST OUTPUT:
------------
Number Five
Number Six
Number Seven
Number Eight

在执行forEach()之前只调用labelData()函数仍然更容易,更高效。 出于好奇,我想出了三种不同的方法来执行此操作,并使用以下代码对一百万个元素流进行了测试:

首先,一种新型的收集器将执行操作并返回原始流:

public interface StreamOpenCollector<T> extends Collector<T, Object, Stream<T>> {   
    static <T> Collector<T, Object, Stream<T>> onOpen(Object arg, Consumer<Object> consumer){
        return Collectors.collectingAndThen(Collectors.toList(), (list) -> {
            consumer.accept(arg);
            return list.stream();
        });
    }   
}

然后是前面提到的OneTimePrinter,然后是测试类:

public class StreamOnOpenTest {
    LinkedHashMap<String, Long> times = new LinkedHashMap<String, Long>();

    public static void main(String[] args) {
        StreamOnOpenTest test = new StreamOnOpenTest();

        test.addTime("SkipMethod", test.time(IntStream.range(0, 1000000).mapToObj(e -> "" + e), (stream) -> {
            stream
                .skip(StreamOnOpenTest.labelData())
                .map(e -> "Number " + e)
                .forEach(System.out::println);              
        }));


        test.addTime("PeekMethod", test.time(IntStream.range(0, 1000000).mapToObj(e -> "" + e), (stream) -> {
            OneTimePrinter printer = new OneTimePrinter();
            stream
                .peek((e) -> printer.print("TEST OUTPUT:\n------------"))
                .map(e -> "Number " + e)
                .forEach(System.out::println);          
        }));

        test.addTime("CollectMethod", test.time(IntStream.range(0, 1000000).mapToObj(e -> "" + e), (stream) -> {
            stream
                .collect(StreamOpenCollector.onOpen("TEST OUTPUT:\n------------", System.out::println))
                .map(e -> "Number " + e)
                .forEach(System.out::println);      
        }));    

        test.printTimes();
    }

    private void addTime(String key, long value){
        times.put(key, value);
    }

    private void printTimes() {
        System.out.println("------------\n------------\n");
        times.forEach((key, value) -> System.out.println("For " + key + ", time was " + value + " milliseconds"));
    }

    private long time(Stream<String> stream, Consumer<Stream<String>> consumer){
        long startTime = System.currentTimeMillis();
        consumer.accept(stream);
        long endTime = System.currentTimeMillis();
        return endTime - startTime;
    }

    private static int labelData(){
        System.out.println("TEST OUTPUT:");
        System.out.println("------------");
        return 0;
    }
}

经过三个运行的最终结果是:

--RUN 1--
For SkipMethod, time was 3479 milliseconds
For PeekMethod, time was 3574 milliseconds
For CollectMethod, time was 3593 milliseconds

--RUN 2--
For SkipMethod, time was 3603 milliseconds
For PeekMethod, time was 3684 milliseconds
For CollectMethod, time was 3718 milliseconds

--RUN 3--
For SkipMethod, time was 3534 milliseconds
For PeekMethod, time was 3540 milliseconds
For CollectMethod, time was 3606 milliseconds

如您所见,skip方法始终更快,但不必担心,除非您正在处理流中的数十亿个项目。

正如已经回答的, .map()是您要寻找的。

但我想指出一些我认为需要做的事情: labelData()firstNames产生了副作用(它看起来像是字段/全局变量)。

对我来说,副作用在函数式编程中并不好,因为它们会引入状态。 这就是流避免出现状态的原因之一。

同样听起来很奇怪,您没有在最后收集流,而是在报告每个元素。 但这可能是由于您的示例简化了。

暂无
暂无

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

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