【问题标题】:How to enhance the performance while dealing with two maps in Java如何在 Java 中处理两个映射时提高性能
【发布时间】:2020-07-14 20:39:14
【问题描述】:

我有两张地图 - Map<String, List<String>> input,另一张是Map<String, List<String>> output

输入地图

{A=[Apple.txt, Axe.txt, Aid.txt], B=[Ball.txt, Boy.txt,Box.txt], C=[Cow.txt,Cob.txt]}

输出地图

{A=[Apple.txt, Axe.txt, Aid.txt], B=[Ball.txt, Boy.txt]}

我需要为输出映射找到缺失的键值对。

 expected output - B= [Box.txt], C=[Cow.txt,Cob.txt]

我需要确定输出映射缺少 B 键的 Box.txt 并且缺少“C”键值对。

我目前的方法:我使用一个 forEach(时间复杂度 O(n))和一个条目集流(时间复杂度:O(m))用于两个导致 O(n*m) 时间复杂度的地图。

inputMap.forEach((key,value) ->
    {
    final List<Path> countrifiedFolderList = outputFileMap.entrySet().stream()
            .filter(entry -> entry.getKey().contains(key))
            .filter(files -> !files.getValue().contains(inputFile)).map(Map.Entry::getKey)
            .collect(Collectors.toList());

    if (!countrifiedFolderList.isEmpty())
    {....do processing
    }

由于地图包含大量数据,我需要增强性能问题。我需要以小于 O(n*m) 的时间复杂度获取结果。

【问题讨论】:

  • 如果您要寻找的条件是entry.getKey().toString().contains(key),那么您将需要一个更复杂的自定义数据结构,而且创建起来并不简单。如果是entry.getKey().equals(key),那就完全不同了。
  • 这里的主要目标是两个比较两个map,在outputMap中找到缺失的键值对
  • 为什么没有A=[], B= [Box.txt], C=[Cow.txt,Cob.txt] 作为最终结果?这可能会在一定程度上简化事情,但是如果不遍历与输入或输出映射中的每个键对应的列表中的每个值,您打算如何识别缺少的内容。完整的遍历必须是 O(n*m) 或者您可能已经从初始输入中选择了数据结构,这样您现在剩下的就是 n*m 遍历?
  • 另外,请注意遍历大小为n 的值List 中的每个元素并在另一个大小为ListList 中执行contains 的复杂性@ 987654339@ 将导致复杂性是n*l。使用Set 应该有助于优化。

标签: java java-8 hashmap java-stream


【解决方案1】:

为什么不:

map1.keySet().containsAll(map2.keySet());

更新

只有一个流:

Map<String, List> result = input.entrySet().stream()
        .filter(entry -> !output.keySet().contains(entry.getKey()) ||
                !output.get(entry.getKey()).containsAll(entry.getValue()))
        .map(entry -> {
                List<String> expected = new ArrayList<>(entry.getValue());
                List<String> current = output.get(entry.getKey());
                expected.removeAll(current != null ? current : List.of());
                return Map.entry(entry.getKey(), expected);
            })
        .collect(Collectors.toMap(Entry::getKey, Entry::getValue));

如果您想衡量性能,我建议您使用您的数据结构、样本量、硬件等进行微基准测试。 如果您对微基准测试感兴趣,我建议您使用JMH

【讨论】:

  • 它返回一个我不需要的布尔值。相反,我需要确定 map2 的缺失值
  • 明白。更新以通过一个流实现您的目标
  • Map&lt;String, List&gt; result = new HashMap&lt;&gt;(map1); map2.forEach((key,value) -&gt; result.get(key).removeAll(value)); result.values().removeIf(Collection::isEmpty);
【解决方案2】:

如果它们是 TreeMap,那么它们的键已经排序。您可以在 O(n) 中同时遍历两个列表。双簧管的解决方案是使用 HashMaps 得到的最好的解决方案,并且将是 O(n*log2(m))。

【讨论】:

    【解决方案3】:

    考虑到output 映射是Map&lt;String, Set&lt;String&gt;&gt;,然后作为最终结果能够将完全存在于输出映射中的键视为空@ 987654323@.

    Map<String, List<String>> lookUpExclusives(Map<String, List<String>> input,
                                                      Map<String, Set<String>> output) {
        return input.entrySet().stream()
                .collect(Collectors.toMap(Map.Entry::getKey,
                        e -> e.getValue().stream()
                                .filter(val -> !output.getOrDefault(e.getKey(),
                                        Collections.emptySet()).contains(val))
                                .collect(Collectors.toList())));
    }
    

    这将从方法返回{A=[], B=[Box.txt], C=[Cow.txt, Cob.txt]}。就复杂性而言,这将是输入映射条目值中每个元素的 M 次数以及每个 N 条目的次数,所以 O(N*M) 也是如此,但这应该是运行时复杂度方面最可能的优化。

    现在您已经有了这个复杂的运行时,您可以进一步链接另一个流操作来过滤结果中没有任何对应值的条目(例如A=[])。这可以通过在第一个 collect 之后将以下代码附加到上述管道来实现:

    .entrySet().stream()
    .filter(e -> !e.getValue().isEmpty())
    .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
    

    它导致的复杂度仅为O(N*M) + O(N),只能有效地表示为O(N*M)。此处的优势在于您可以按照您所期望的格式获得结果,例如 {B=[Box.txt], C=[Cow.txt, Cob.txt]}

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2023-02-07
      • 1970-01-01
      • 2019-09-14
      • 2014-09-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多