【问题标题】:Insertion in HashMap vs HashsetHashMap 与 Hashset 中的插入
【发布时间】:2023-03-16 20:00:01
【问题描述】:

HashMap 的情况下,如果 key 重复,它会用新值替换旧值。HashSet 的情况下,根本不插入项目。由于HashSet 在内部实现HashMap。为什么这两个插入的处理方式不同?

【问题讨论】:

  • 你在这里比较苹果和橘子。 HashMaps 像 Maps 一样工作,HashSets 像 Sets 一样工作。 Maps 和 Sets 是两个完全不同的概念,您不应该让 HashSet 可能在 HashMap 之上实现这一事实导致您不相信。
  • 你被糟糕的措辞弄糊涂了。 “HashMap 替换旧的 Key 值” 正确/错误。它替换了旧的 value,但 key 没有改变。 HashSet 实际上实现为 HashMap,其中 value 被忽略,即添加到 Set 的任何值都作为 key 放入映射中具有虚拟值:public boolean add(E e) { return map.put(e, PRESENT)==null; }
  • 所以我可以改写它,因为“HashMap 中的重复键”和“HashSet 中的重复值”都不会覆盖以前的键,并且一旦找到重复键(HashMap)就会返回 false或值(HashSet)。

标签: java collections hashmap hashset


【解决方案1】:

HashMap 是用新的 value 替换旧的,当您使用相同的键插入它时。 示例:

Map<Integer, String> map = new HashMap<>();
map.put(1, "first put");
System.out.println(map.get(1)); // <-- prints `first put`
map.put(1, "second put");
System.out.println(map.get(1)); // <-- prints `second put`

所以基本上当新值到达时带有一个已经存在于映射中的键,它只是替换这个键的值,因为即使键是相同的,新值也可以不同于与这个键关联的值地图。

如果是HashSet,只有key,没有价值。因此,当已经存在的key 到达时,没有什么可做的,它已经存在了。

关于HashSet的内部实现,它确实在内部使用HashMap,但它的作用是创建一个object

// Dummy value to associate with an Object in the backing Map
private static final Object PRESENT = new Object();

当你设置add 一些key 时,它只是在这个地图上调用put(key, present),对所有条目使用相同的对象(为了不分配大量无用的对象:

public boolean add(E e) {
    return map.put(e, PRESENT)==null;
}

总而言之,HashSet确实在内部使用HashMap,当您将某些内容放入哈希映射时,总是分配新值而不是旧值,但是key不是,并且作为@ 987654336@ 使用它的键作为 HashMap 中的键,那么如果你把重复的键放在HashSet 它不会删除旧的。 示例:

假设我们有一些实体类,它有 2 个字段,int id 和 string name,但只有 id 参与了 equals 方法的实现。

    Entity entity1 = new Entity(1, "some name");
    Entity entity2 = new Entity(1, "some other name");
    System.out.println(entity1.equals(entity2)); // returns true

    Map<Integer, Entity> map = new HashMap<>();
    map.put(entity1.id, entity1);
    map.put(entity2.id, entity2);
    System.out.println(map.get(entity1.id).name); // returns "some other name"

    Map<Entity, String> keyMap = new HashMap<>();
    keyMap.put(entity1, entity1.name);
    keyMap.put(entity2, entity2.name);
    System.out.println(keyMap.keySet()); // returns [Entity{id=1, name='some name'}]
    System.out.println(keyMap.values()); // returns [some other name]

    Set<Entity> set = new HashSet<>();
    set.add(entity1);
    set.add(entity2);
    System.out.println(new ArrayList<>(set)); // returns [Entity{id=1, name='some name'}]

你也不应该依赖它,因为它的内部实现细节可以在未来的版本中改变,HashMapHashSet 使用equals 方法检查对象是否相同。如果您有一个 equals 返回 true 的两个对象,但您仍然希望区别对待,那么您的实体设计很可能存在一些问题,但是在极少数情况下,当您确定这是所需的效果时,在这种情况下您可以看看IdentityHashMap

【讨论】:

  • 感谢您的深入解释!不过,只有一个问题,重复的 Key 值是否会被前一个值覆盖,或者它会在找到重复项后立即返回。
  • @AnkushDutt 正如我提到的 PRESENT 对象是静态的,这意味着 HashSet 的所有实例都会将此特定的 Object 实例作为底层映射的值,当您将调用 addHashSet 上,从HashMap 的当前实现中,我看到它确实会重新分配新值,即使它们相等,但你不应该依赖它,因为它是内部实现细节。但是,如果 key 已经存在,则不会重新分配它。如果分配了哪个对象对您很重要,即使它们相等,那么您可能应该看看IdentityHashMap
  • @AnkushDutt 我用更多的解释和例子更新了答案
  • 所以我可以改写它,因为“HashMap 中的重复键”和“HashSet 中的重复值”都不会覆盖以前的键,并且一旦找到重复键(HashMap)就会返回 false或值(HashSet)。
  • @AnkushDutt 是的,如果HashSet 键不会被覆盖,HashMap 键也不会被覆盖,但是值会被覆盖。您可以在我的答案中查看最后一个示例,它显示了所有情况的这种行为
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-11-22
  • 2022-01-05
  • 2011-02-15
  • 2010-12-10
相关资源
最近更新 更多