【问题标题】:Merging multiple maps in Java 8在 Java 8 中合并多个地图
【发布时间】:2018-04-17 10:46:20
【问题描述】:

这里是 Java 8。我有以下课程:

public interface Animal {
  ...
}

public class Dog implements Animal {
  ...
}

public class Cat implements Animal {
  ...
}

public class Elephant implements Animal {
  ...
}

我必须实现以下方法:

void doSomething(Map<String,Dog> dogs, Map<String,Cat> cats, Map<String,Elephant> elephants) {
  // TODO:
  // * Merge all dogs, cats & elephants together into the same Map<String,Animal>,
  //     but...
  // * Do so generically (without having to create, say, a HashMap instance, etc.)
}

在我的doSomething(...) 方法中,我需要将所有映射参数合并到同一个Map&lt;String,Animal&gt; 映射中,但是我真的更愿意在没有我的代码的情况下这样做 必须实例化特定的地图实现(例如HashMap 等)。

意思是,我知道我可以这样做:

void doSomething(Map<String,Dog> dogs, Map<String,Cat> cats, Map<String,Elephant> elephants) {
  Map<String,Animal> animals = new HashMap<>();
  for(String dog : dogs.keySet()) {
    animals.put(dog, dogs.get(dog));
  }
  for(String cat : cats.keySet()) {
    animals.put(cat, cats.get(cat));
  }
  for(String elephant : elephants.keySet()) {
    animals.put(elephant, elephants.get(elephant));
  }

  // Now animals has all the argument maps merged into it, but is specifically
  // a HashMap...
}

如果它存在的话,我什至可以使用一些实用程序,比如Collections.merge(dogs, cats, elephants) 等。有什么想法吗?

【问题讨论】:

  • 为什么要避免实例化新地图?
  • 感谢@shmosel,但不是在这里寻找 XY 答案。
  • 我实际上是在尝试理解需求。不是要创建一个新对象吗?不使用特定的实现?使用与参数相同的实现?支持各种实现?目前还不是很清楚你的目标是什么。请注意,到目前为止提供的所有答案实际上都实例化了一个新地图。

标签: java dictionary collections java-8


【解决方案1】:

一种方法是创建一组条目流,然后将其平面映射为具有条目流并将它们收集到地图中。

Map<String,Animal> animals = 
    Stream.of(dogs.entrySet(), cats.entrySet(), elephants.entrySet())
          .flatMap(Set::stream)
          .collect(toMap(Map.Entry::getKey, Map.Entry::getValue));

//Stream.of(dogs, cats, elephants).flatMap(m -> m.entrySet().stream()) could also be an option

也不是单行且没有使用Map#putAll 的流:

Map<String,Animal> animals = new HashMap<>(dogs);
animals.putAll(cats);
animals.putAll(elephants);

【讨论】:

  • 谢谢@Alexis C (+1) 但是你的toMap(...) 静态方法来自哪里?
  • @smeeb java.util.stream.Collectors
  • 我会做一点小改进:Stream.of(dogs, cats, elephants).map(Map::entrySet)
  • @shmosel 但是你需要对流进行平面映射,即Stream.of(dogs, cats, elephants).map(Map::entrySet).flatMap(Set::stream)。在这种情况下,我更喜欢直接写Stream.of(dogs, cats, elephants).flatMap(m -&gt; m.entrySet().stream()),但我猜是口味问题。
  • @smeeb 来自Collectors class。如果你想有一个特定的地图实现,你可以使用这个overloaded method
【解决方案2】:

您可以使用Stream.Concat 完成手头的任务:

Map<String,Animal> animals = Stream.concat(Stream.concat(dogs.entrySet().stream(), cats.entrySet().stream()), elephants.entrySet().stream())
                                   .collect(Collectors.toMap
                                    (
                                       Map.Entry::getKey,
                                       Map.Entry::getValue
                                    )
                              );

您需要在这里小心,因为当合并并且有重复的键时,会按预期引发异常。

【讨论】:

  • 感谢@Aomine (+1) - 但您的代码会产生以下错误:“无法从静态上下文引用非静态方法”...有什么想法吗?
  • 这很可能是因为您在静态上下文中调用方法doSomething。这与手头的解决方案无关。 2种方法解决。 1 是实例化一个包含类型的对象,然后从新实例中调用doSomething 或将doSomething 方法设为静态。
  • 好的,但是doSomething(...) 不是静态方法。我会开始玩弄它。
  • 是的,我在关注你,还有其他事情正在发生。这是一个编译器(不是运行时)错误,这是我正在构建的一个库(所以我在代码中没有任何其他地方明确调用了 doSomething(...))。
【解决方案3】:

IMO 的最佳方式是在Alexis C.'s answer 中使用Map.putAll

Map<String, Animal> animals = new HashMap<>(dogs);
animals.putAll(cats);
animals.putAll(elephants);

这个的一个变种:

Map<String, Animal> animals = new HashMap<>(dogs);
cats.forEach(animals::put);
elephants.forEach(animals::put);

【讨论】:

  • 感谢@Federico,但这仍然需要我将animals 实例化为HashMap。听起来他们是避免这种情况的其他(基于流的)方法,所以我对这些更感兴趣。
  • @smeeb 没有记录,但toMap() 也会产生HashMap
猜你喜欢
  • 2017-03-02
  • 2014-08-09
  • 1970-01-01
  • 2012-06-16
  • 1970-01-01
  • 1970-01-01
  • 2020-01-18
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多