【问题标题】:Time complexity of stream filter流过滤器的时间复杂度
【发布时间】:2018-01-22 20:43:48
【问题描述】:

我有这样的代码:

List<Listing> Listings = new ArrayList<>();
Listings.add(listing1);
Listings.add(listing2);
...
...
...

Listing listing= listings.stream()
                .filter(l -> l.getVin() == 456)
                .findFirst();

我的问题是过滤过程的时间复杂度是多少?如果是 O(n),我的直觉是把它转换成类似 HashSet 的数据结构,这样时间复杂度就可以变成 O(1),有没有一种优雅的方法可以用流做到这一点?

【问题讨论】:

  • 它可能是一个并行流,但复杂度仍然是 O(n)。将它转换为一个过滤器操作的集合仍然是 O(n) 所以你首先需要使用一个集合。您可能希望使用 LinkedHashset 来保持插入顺序或 TreeSet 用于其他排序(列表意味着某些排序和/或允许重复)。
  • Stream#filter 始终遍历完整的Stream 并将过滤条件应用于每个元素。一个优点是流可以很容易地并行化,但这只会将时间复杂度降低一个常数因子(物理内核的数量)。

标签: java performance java-8 java-stream


【解决方案1】:

它是O(n)。流过滤在内部使用迭代。

您可以将其转换为地图,如下所示:

Map<Integer, Listing > mapOfVinToListing = listings.stream().collect(Collectors.toMap(Listing::getVin, Functions.identity()); // Assuming vin is unique per listing
mapOfVinToListing.get(456);// O(1)

但是,这个转换过程也是 O(n)。因此,如果您只需要这样做一次,请使用过滤器。如果您需要多次查询同一个列表,那么将其转换为地图可能是有意义的。

您也可以尝试使用并行流。在某些情况下,它们的性能可能更高,但这在很大程度上取决于具体情况。

【讨论】:

  • 或者,如果可能的话,直接从头开始使用索引HashMap 将元素存储在那里。如果您需要经常进行此类查询,那么像@Adam 所说的那样,为列表编制索引是一个更好的解决方案。
  • @Zabuza 好点。如果可以将集合创建为 HashMap,那肯定比转换它更好。
【解决方案2】:

最坏的情况是O(n) 但由于Stream 是惰性的,如果之前找到该值,它将停止迭代。如果您需要不断的查找时间,那么转换为Map 是一个好主意,但代价是额外的空间;如果列表很大,则应考虑该方面。事实上,如果列表很小,MapList 之间的差异几乎不会引起注意,除非您在时间紧迫的系统中工作。

【讨论】:

    【解决方案3】:

    filter 本身没有终端操作的开销为零——因为它绝对什么都不做;流仅由终端操作驱动 - 没有终端操作,不执行任何操作。

    然后是filter 必须迭代的所有元素(可能是所有元素)(懒惰地)。因此过滤器的时间复杂度将取决于您从中流式传输的源;在你的情况下List,所以它会是O(n)

    但那将是最坏的情况。就我所见filter一般而言,您无法预测平均情况,因为它取决于基础来源。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2018-07-18
      • 1970-01-01
      • 1970-01-01
      • 2016-02-13
      • 2012-08-14
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多