【问题标题】:Is there an efficient way of checking if HashMap contains keys that map to the same value?是否有一种有效的方法来检查 HashMap 是否包含映射到相同值的键?
【发布时间】:2017-01-08 02:05:35
【问题描述】:

我基本上需要知道我的 HashMap 是否有不同的键映射到相同的值。我想知道除了对照地图中的所有其他值检查每个键值之外,是否还有其他方法。

更新: 只是一些更多的信息,希望能澄清我想要完成的事情。考虑一个字符串“azza”。假设我正在迭代这个字符串并将每个字符存储为一个键,它的对应值是其他一些字符串。假设我最终到达了“a”的最后一次出现,并且该值已经在地图中。如果与地图中已经存在的值对应的键也是“a”,那就没问题了。当“a”和“z”都映射到相同的值时,就会出现我的问题。仅当不同的键映射到相同的值时。

【问题讨论】:

  • 获取值集合,将其转换为HashSet,查看集合大小是否小于值集合大小。这假设值对象具有合适的 equals()hashCode() 实现。
  • 经过深思熟虑后,我认为如果不简单地检查每个值与所有其他值,我想要完成的工作是不可能的。例如,如果我有字符串“dddd”。当我遍历字符串并将每个字符映射到其他字符串时,如果所有出现的“d”都映射到诸如“apples”之类的值,那就没问题了。我的地图最终会得到一个键和一个值。只有当“ddad”之类的东西和“a”和“d”都映射到“apples”时

标签: java hashmap


【解决方案1】:

当然,编码和执行速度最快的是:

boolean hasDupeValues = new HashSet<>(map.values()).size() != map.size();

在 O(n) 时间内执行。

集合不允许重复,因此如果有重复,集合将小于值列表。

【讨论】:

  • 受限于我提到的警告。
  • 我最终使用了你和 EJP 提到的 Set,我只需要使用一些创造性的条件。谢谢。如果 HashMaps 有一个 .containsValue() 方法,那就太酷了。这正是我所希望的,一个具有这样一个功能的库,这样我就不必在 Set 中使用额外的空间了。
  • @ElroyJetson 是什么让你认为HashMap 没有containsValue 方法?
  • @LouisWasserman 提出了反问,containsValue() 将如何提供帮助?它会为每个值返回 true(当然),因此查找重复项没有用处。在每次插入之前使用防止重复可能很有用,但您询问了查找重复。
  • 是的,在插入之前会很有用,我同意我的问题可以更好地表述。
【解决方案2】:

您可以创建一个HashMap,将值映射到键列表。这将占用更多空间并需要(稍微)更复杂的代码,但具有更高效率的好处(对于仅循环所有值的方法,摊销 O(1) 与 O(n))。

例如,假设您当前拥有HashMap&lt;Key, Value&gt; map1,并且您想知道哪些键具有相同的值。你创建另一个地图,HashMap&lt;Value, List&lt;Key&gt;&gt; map2

那你只需修改map1map2 一起。

map1.put(key, value);
if(!map2.containsKey(value)) {
    map2.put(value, new ArrayList<Key>);
}
map2.get(value).add(key);

然后要获取映射到value 的所有键,您只需执行map2.get(value)

如果您需要在许多不同的地方放置/删除,以确保您不会忘记使用map2,您可以创建自己的包含 2 个映射的数据结构(即单独的类)并实现 put/删除/获取/等。为此。


编辑:我可能误解了这个问题。如果您不需要实际的键列表,只需一个简单的“是/否”回答“地图是否已经包含此值?”,并且您想要比 O(n) 更好的东西,您可以保留一个单独的 @ 987654330@ 只是计算该值在地图中出现的次数。这将比列表映射占用更少的空间。

【讨论】:

  • 是的,我只需要一个简单的是或否。我更新了我的问题,我开始认为没有其他方法可以完成我想要完成的事情。
【解决方案3】:

与上面的 EJP 和 Bohemian 的答案非常相似,但带有流:

boolean hasDupeValues = map.values().stream().distinct().count() != map.size();

【讨论】:

    【解决方案4】:

    您可以通过调用map.values().contains(value) 来检查映射是否已包含值。这不如在 map 中查找 key 高效,但仍然是 O(n),并且您不需要创建一个新集合来计算它的元素。

    但是,您似乎需要的是BiMap。 Java 标准库中没有这样的东西,但是您可以通过使用两个HashMaps 相对容易地构建一个:一个将键映射到值,另一个将值映射到键。每次你将一个键映射到一个值时,你可以检查该值是否已经被映射到的摊销 O(1),如果不是,则将键映射到一个映射中的值,并将值映射到键入另一个。

    如果可以选择为您的项目创建新依赖项,一些第三方库包含现成的 bimap,例如 Guava (BiMap) 和 Apache Commons (BidiMap)。

    【讨论】:

    • map.values().contains(value) 完全等同于 map.containsValue(value)
    【解决方案5】:

    您可以遍历键并将当前值保存在 Set 中。 但是,在将该值插入 Set 之前,请检查 Set 是否已包含该值。

    如果为真,则表示前一个键已经包含相同的值。

    Map<Integer, String> map = new HashMap<>();
    Set<String> values = new HashSet<>();
    Set<Integter> keysWithSameValue = new HashSet<>();
    
    for(Integer key : map.keySet()) {
      if(values.contains(map.get(key))) {
        keysWithSameValue.add(key);
      }
      values.add(map.get(key));
    }
    

    【讨论】:

    • 没错,我们甚至可以使用ConcurrentHashMap中的putIfAbsent方法。我们可以检查返回值——如果它不同于null,我们将它添加到keysWithSameValue
    • 我也想过这个。问题是当我向我的地图添加键和值时,我可能会遇到我已经添加的键值对。该集合会告诉我我有一个问题,因为该值已经发生,但实际上我添加具有相同 Pair 的相同 Key 对于我正在尝试做的事情很好。
    • 我从来不知道这个 putIfAbsent() 方法!谢谢!
    猜你喜欢
    • 1970-01-01
    • 2014-09-14
    • 2019-05-13
    • 2019-10-21
    • 1970-01-01
    • 2023-03-17
    • 2013-05-28
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多