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

时间:2017-05-29 08:25:36

标签: java java-stream

我有以下代码:

<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.4/angular.min.js"></script>
<div ng-app="app" ng-controller="myCtrl">
  <select map-value name="traveller_type" class="full-width" ng-model="traveller_type" ng-options="
                    item as item.value for item in personalityFields.traveller_type track by item.value">
    <option value="" disabled selected> Choose ...</option>
  </select>
  {{traveller_type}}
</div>

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

这是一个更好的,性能明智的做一个for循环然后,所以我只循环一次?我发现这些流更具可读性。

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

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);

并且正如预期的那样,for循环总是比流

更快
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;
}
}

但从来没有显着。

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

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

1 个答案:

答案 0 :(得分:5)

你不会三次遍历这个系列。

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);

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

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

使用常规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;
  }
}

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