我应该多次流式传输还是在一个流中进行所有计算?

Som*_*era 8 java java-stream

我有以下代码:

mostRecentMessageSentDate = messageInfoList
    .stream()
    .findFirst().orElse(new MessageInfo())
    .getSentDate();

unprocessedMessagesCount = messageInfoList
    .stream()
    .filter(messageInfo -> messageInfo.getProcessedDate() == null)
    .count();

hasAttachment = messageInfoList
    .stream()
    .anyMatch(messageInfo -> messageInfo.getAttachmentCount() > 0);
Run Code Online (Sandbox Code Playgroud)

如您所见,我将相同的列表流式传输3次,因为我想找到3个不同的值.如果我在For-Each循环中执行此操作,我只能循环一次.

这样做是否更好,性能明智的循环呢,所以我只循环一次?我发现这些流更具可读性.

编辑:我运行了一些测试:

import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

public class Main {

public static void main(String[] args) {

    List<Integer> integerList = populateList();

    System.out.println("Stream time: " + timeStream(integerList));
    System.out.println("Loop time: " + timeLoop(integerList));

}

private static List<Integer> populateList() {
    return IntStream.range(0, 10000000)
            .boxed()
            .collect(Collectors.toList());
}

private static long timeStream(List<Integer> integerList) {
    long start = System.currentTimeMillis();

    Integer first = integerList
            .stream()
            .findFirst().orElse(0);

    long containsNumbersGreaterThan10000 = integerList
            .stream()
            .filter(i -> i > 10000)
            .count();

    boolean has10000 = integerList
            .stream()
            .anyMatch(i -> i == 10000);

    long end = System.currentTimeMillis();

    System.out.println("first: " + first);
    System.out.println("containsNumbersGreaterThan10000: " + containsNumbersGreaterThan10000);
    System.out.println("has10000: " + has10000);

    return end - start;
}

private static long timeLoop(List<Integer> integerList) {
    long start = System.currentTimeMillis();

    Integer first = 0;
    boolean has10000 = false;
    int count = 0;
    long containsNumbersGreaterThan10000 = 0L;
    for (Integer i : integerList) {
        if (count == 0) {
            first = i;
        }

        if (i > 10000) {
            containsNumbersGreaterThan10000++;
        }

        if (!has10000 && i == 10000) {
            has10000 = true;
        }

        count++;
    }

    long end = System.currentTimeMillis();

    System.out.println("first: " + first);
    System.out.println("containsNumbersGreaterThan10000: " + containsNumbersGreaterThan10000);
    System.out.println("has10000: " + has10000);

    return end - start;
}
}
Run Code Online (Sandbox Code Playgroud)

正如预期的那样,for循环总是比流更快

first: 0
containsNumbersGreaterThan10000: 9989999
has10000: true
Stream time: 57
first: 0
containsNumbersGreaterThan10000: 9989999
has10000: true
Loop time: 38
Run Code Online (Sandbox Code Playgroud)

但从来没有显着.

findFirst可能是一个不好的例子,因为它只是在流是空的时候退出,但我想知道它是否有所作为.

我希望得到一个允许从一个流中进行多次计算的解决方案.IntSummaryStatistics并不完全符合我的要求.我想我会留意@ florian-schaetz并坚持赞成可读性以获得边际性能提升

Mag*_*lex 5

您不会遍历集合三次.

mostRecentMessageSentDate = messageInfoList
        .stream()
        .findFirst().orElse(new MessageInfo())
        .getSentDate();
Run Code Online (Sandbox Code Playgroud)

以上检查集合中是否有任何元素,并根据此值返回值.它不需要经历整个集合.

unprocessedMessagesCount = messageInfoList
        .stream()
        .filter(messageInfo -> messageInfo.getProcessedDate() == null)
        .count();
Run Code Online (Sandbox Code Playgroud)

这个需要过滤掉没有进程日期的所有元素并对它们进行计数,因此这个元素遍历整个集合.

hasAttachment = messageInfoList
        .stream()
        .anyMatch(messageInfo -> messageInfo.getAttachmentCount() > 0);
Run Code Online (Sandbox Code Playgroud)

以上只需要遍历元素,直到找到带附件的消息.

因此,在三个流中,只需要其中一个流经过整个集合,在最坏的情况下,您需要进行两次迭代(第二次,可能是第三次流).

使用常规For-Each循环可能会更高效,但你真的需要它吗?如果你的集合只包含一些对象,我就不会打扰它了.

但是,使用传统的For-Each循环,您可以组合最后两个流:

int unprocessedMessagesCount = 0;
boolean hasAttachment = false;

for (MessageInfo messageInfo: messageInfoList) {
  if (messageInfo.getProcessedDate() == null) {
    unprocessedMessagesCount++;
  }
  if (hasAttachment == false && messageInfo.getAttachmentCount() > 0) {
    hasAttachment = true;
  }
}
Run Code Online (Sandbox Code Playgroud)

如果您认为这是一个更好的解决方案(我也发现流更具可读性),这取决于您.我没有看到将三个流合并为一个的方法,至少不是以更易读的方式.

  • 可能有一种方法通过滥用`map`或类似方法用流写入它,但我高度怀疑它会是一个好方法.检查你的代码是否真的处于运行时优化的NEED中,否则请记住:过早优化是所有邪恶的根源 - 在这种情况下,你会牺牲解决非问题的可读性.只有当它实际上是一个性能问题时,重构它,在这种情况下@Magnilex解决方案似乎比我能想到的任何流版本更易读. (4认同)