【问题标题】:Performing a UNION operation among keys of N hash tables在 N 个哈希表的键之间执行 UNION 操作
【发布时间】:2016-12-19 12:19:15
【问题描述】:

我有一个包含 N 个 hast 表的哈希表:

Map<Integer,Map<String,Double>

我需要创建一个包含内部映射的所有键的列表:

----------------
|     | a    2 |
| 100 | b    1 |    
|     | c    2 |
----------------
|     | a    2 |
| 101 | d    2 |     
---------------- 
|     | a    2 |
| 102 | b    1 |    
|     | e    2 |
----------------

列表 = {a,b,c,d,e}

这是我当前的代码:

Set<String> keys= new HashSet<>();
    map1.entrySet().forEach(e -> {
        keys.addAll(e.getValue().keySet());
    });

map1 包含数千个条目。

这是最佳方法吗?有人知道更快的方法吗?

【问题讨论】:

  • 你为什么不使用map.entrySet().stream().map(Map.Entry::getKey).collect(Collectors.toList()); ?这样你就不需要嵌套的addAll() 调用,也不需要在之前初始化一个空列表。那当然只是化妆品。但是:我不知道在流上使用.parallel() 是否会提高性能。
  • @JDC 我正在测试你的解决方案,但是Collectors 无法解决
  • 你必须将它添加到你的类的导入语句中:import java.util.stream.Collectors;
  • @JDC 已经导入了,因为我已经用过了
  • @PatrickParker:还没有答案,而是他为什么不使用它的问题。但我会添加它作为答案。

标签: java hashmap


【解决方案1】:

您可以尝试使用以下代码:

Map<String, Double> innerMap = new HashMap<>();
innerMap.put("a", 2d);
innerMap.put("b", 2d);
innerMap.put("c", 2d);

Map<String, Double> innerMap2 = new HashMap<>();
innerMap2.put("a", 2d);
innerMap2.put("d", 2d);
innerMap2.put("e", 2d);

Map<Integer, Map<String, Double>> map = new HashMap<>();
map.put(100, innerMap);
map.put(101, innerMap2);

Set<String> collect = map.values()
                         .stream()
                         .parallel()
                         .map(Map::keySet)
                         .flatMap(Collection::stream)
                         .collect(Collectors.toSet());

很遗憾,如果它对性能有重大影响,您将不得不自己尝试。


这意味着您使用的是 Java 8。但是当您使用forEach() 方法时,我只是假设。


编辑:
请注意D. Kovács关于parallel()方法使用的评论:Details

【讨论】:

  • 不要使用并行,没有适当的理由。有关详细信息,请参阅this Gist,即使开始考虑并行流,也应该满足 TL;DR: NQ >= 10'000(N:数据点数;Q:工作量)。在这种情况下,所有地图中的 N == 条目; Q ≈ 1。
  • @D.Kovács 有趣,不知道!
  • @SME_Dev 你是对的,相应地更新了代码
  • @JDC 纠正我如果我错了,但 toSet 的文档说“没有保证返回的 Set 的类型、可变性、可序列化性或线程安全性”......所以这不违反您对parallel() 的使用吗?
  • @PatrickParker 我发现以下帖子可以解决您的问题:stackoverflow.com/questions/22350288/…
【解决方案2】:

基本上,您的方法最适合一般情况。如果您对 Map 结构、密钥分布等有额外的限制,您当然可以对其进行定制。但是对于一般情况,我没有看到更好的方法,也许是其他语法(例如,一直使用流:

map.entrySet()
   .stream()
   .map(Map.Entry::getValue)
   .flatMap(e -> e.keySet().stream())
   .collect(Collectors.toSet())

)

【讨论】:

    【解决方案3】:

    我认为您可以通过使用 flatMap() 来充分利用并行性

    如果您可以估计所需的大小,您可能还希望考虑 ConcurrentHashMap,即Collections.newSetFromMap(new ConcurrentHashMap())

    例子:

        final int EST_SIZE = 6_000_000;
        // Map<Integer,Map<String,Double>> map1;
        Set<String> keys = map1.values().stream().map(Map::keySet)
                .flatMap(Set::stream).parallel().unordered()
                .collect(Collector.of(
                        () -> Collections.newSetFromMap(
                            new ConcurrentHashMap<>(EST_SIZE * 4 / 3 + 1)
                        ),
                        Set::add,
                        (set1,set2) -> { 
                            set1.addAll(set2);
                            return set1;
                        },
                        Collector.Characteristics.CONCURRENT,
                        Collector.Characteristics.UNORDERED
                        ));
    

    注意:以上代码是针对高度专业化场景的尝试优化。在决定之前对其进行测试并检查性能。在一般情况下,您应该更喜欢这样的东西:

        Set<String> keys = map1.values().stream().map(Map::keySet)
                .flatMap(Set::stream).collect(Collectors.toSet());
    

    【讨论】:

    • 使用 ConcurrentSkipListSet 的任何具体原因?它是最慢的 Set 实现之一,仅应在合理的情况下使用。也不要使用并行流,除非有正当理由:)
    • @D.Kovács 好吧,OP 确实说了“数千”张地图,因此这可能意味着数千 x 数千个键。他还将他的问题描述为一个非常大的数据集的性能问题,这可以从并行性中受益。当然,如果没有他的数据,我无法对其进行测试以确定。对于您的其他问题,请参阅stackoverflow.com/a/6720658/7098259
    • 很公平,但即使是几百万个条目,它也不应该超过几秒钟,没有并行性。我不得不承认,我不喜欢并行流(请参阅我的其他评论以及有关该主题的链接 Gist)。我只是感觉这些添加(即并行 + CSkipLSet)是过早的优化。
    • @D.Kovács 我对 600 万个键的高度不科学的测试显示出显着的加速。 ConcurrentHashMap = 1971 (millis), Collectors.toSet = 3354 (millis)
    • 确实,这就是我所期望的。那是1秒的差异。如果您每隔几秒钟或每分钟执行一次,那么您就会对此感兴趣(并行会带来很多麻烦,例如,由于常见的 ForkJoinPool)。如果你在懒惰的时候每天做一次(没有服务器负载时的统计数据),那么谁在乎呢? :)
    猜你喜欢
    • 2012-05-23
    • 2013-07-03
    • 1970-01-01
    • 2012-02-05
    • 2011-07-22
    • 1970-01-01
    • 2010-10-24
    • 2011-07-01
    • 2011-08-16
    相关资源
    最近更新 更多