【问题标题】:Which is better, Stream or Iterator [closed]哪个更好,流或迭代器[关闭]
【发布时间】:2017-08-06 09:09:20
【问题描述】:

我在玩 Java * Stream API,我在 Lagecy 系统中有以下代码:

Iterator itr = configMap.entrySet().iterator();
        List<String> resultList = new ArrayList<String>();
        while(itr.hasNext()){
            Entry e = (Entry)itr.next();
            Config rc = (Config)e.getValue();

            if (rc != null && rc.getVisibleTo() != null && (rc.getVisibleTo().equalsIgnoreCase("0010") || rc.getVisibleTo().equalsIgnoreCase("0111") || rc.getVisibleTo().equalsIgnoreCase("0011"))){        
                resultList .add(rc.getName());
            }
        }

我写了上面代码的Stream Equivalent如下:

List<String> resultList = configMap.entrySet()
                   .parallelStream()
                   .map(r -> r.getValue())
                   .filter(r -> r.getVisibleTo() != null)
                   .filter(r -> {return 
                              r.getVisibleTo().equalsIgnoreCase("0010")
                           || r.getVisibleTo().equalsIgnoreCase("0111")
                           || r.getVisibleTo().equalsIgnoreCase("0011");
                        })
                   .map(r -> r.getName())
                   .collect(Collectors.toList());

两种方式我都得到了想要的结果。我的问题是在这种情况下哪种表现方式更好?如果我选择其中一个而不是另一个,我真的会获得任何价值吗?地图中有大约 1000 个值。

【问题讨论】:

  • 这看起来像是基于意见的问题。有人会说a很好。有人会说b好。它可能会在人们之间引起争论并影响本网站。它也可能会让你投反对票(不是来自我)。所以尝试编辑你的问题
  • 为了可读性,我会从 .filter(r -> r.getVisibleTo() != null) .filter(r -> {return r.getVisibleTo().equalsIgnoreCase("0010) 创建一个单独的谓词") || r.getVisibleTo().equalsIgnoreCase("0111") || r.getVisibleTo().equalsIgnoreCase("0011"); })
  • 我会采用更具可读性的方法(即流方法)
  • 我真的很喜欢 equalsIgnoreCase 的这种用法。你永远不知道,也许那里有小写的0s 或大写的1s……
  • 您最好专注于编写清晰、明显和可维护的代码,而不是关注微观性能问题。在绝大多数情况下,显而易见的代码也足够快。

标签: java-8


【解决方案1】:

我会稍微重写sream 示例:

List<String> resultList = configMap.entrySet()
        .parallelStream()
        .map(Map.Entry::getValue)
        .filter(Objects::nonNull) // value might be a null
        .filter(r ->
                ((Predicate<String>) "0010"::equals)
                        .or("0111"::equals) // null-save equivalent to  "0111".equals(value)
                        .or("0011"::equals)
                        .test(r.getVisibleTo())
        )
        .map(Config::getName)
        .collect(Collectors.toList());

并将使用它。 详细的性能解释请看这些问题Java 8: performance of Streams vs CollectionsIterator versus Stream of Java 8

正如@4castle 在下面的 cmets 中所建议的那样,我们可以通过使用 configMap.values() 而不是 configMap.entrySet() 来缩短它:

List<String> resultList = configMap.values()
        .parallelStream()
        .filter(Objects::nonNull)
        .filter(r ->
                ((Predicate<String>) "0010"::equals)
                        .or("0111"::equals) // null-save equivalent to  "0111".equals(value)
                        .or("0011"::equals)
                        .test(r.getVisibleTo())
        )
        .map(Config::getName)
        .collect(Collectors.toList());

【讨论】:

  • 空检查也没有得到正确处理。也许将它们放在单独的.filter 中会更简单。
  • @4castle,你的意思是一个额外的过滤器来检查map.value是否是null
  • 是的,还有对.getVisibleTo()的空检查,因为它使用or而不是and
  • @4castle,是的。你完全正确。我会更新我的答案。非常感谢您发现这一点。我想我们真的不需要检查getVisibleTo 是否是null,因为"0010"::equalsnull-safe 比较。我们只需要检查一个值本身是否不为空。
  • 值得一提的是,您可以首先通过 configMap.values() 流式传输来简化代码,但重复原始代码而不做任何更改并不能真正说明这一点。除此之外,没有理由将.filter(Objects::nonNull) 插入原始代码中不存在的链中。这充其量是误导。
【解决方案2】:

首先,如果你关心性能,你应该根据事实来决定,所以衡量,不要猜测(即使用JMH)。

对于简单的情况,迭代或经典循环往往比 Streams 更快。另一方面,流可以更快,并且通常可以通过并行性进一步加速(其中工作负载很容易并行化)。在大多数情况下,这并不重要(根据我的经验),因为数据集太小和/或使用它的代码对性能不够关键。

关于可读性/可维护性,我倾向于使用流/功能方法来更好地分离关注点。但是 for-each 循环也可能会改进您的迭代代码。所以没有简单的是/否答案,这完全取决于上下文。

【讨论】:

  • 有了足够的数据,流通常比迭代更快(即使在顺序执行中)。流使用的迭代协议 (Spliterator) 的每个元素开销远低于 Iterator
【解决方案3】:

由于您将其编写为并行流,它尝试多线程并划分工作,因此您可能会看到由于管理线程的开销而导致性能下降,因为您的工作有点小。这里的另一个陷阱是,由于您使用的是CommonForkJoinPool(此线程池的默认线程池),这不是一个很好的选择,因为用任务阻塞它会降低整个应用程序的性能(虽然与工作量小,请牢记)。

话虽如此,如果您改用单线程configMap.entrySet().stream(),我会说性能差异可能会稍微慢一些,但可读性足以弥补它。因此,选择变成了在稍微更快的代码还是更好的可读性之间做出的决定,这完全取决于您(但我个人会选择流)。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-03-19
    • 2011-01-05
    • 2010-11-24
    • 2013-08-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-06-28
    相关资源
    最近更新 更多