【问题标题】:Can I retrieve a key stored in a Hashset in java in some way?我可以以某种方式检索存储在 java 中的 Hashset 中的密钥吗?
【发布时间】:2014-04-07 14:35:02
【问题描述】:

我正在尝试执行双向 A 星搜索,我在哪里遇到了这个问题。

假设我在哈希集 H 中有一个对象 A。假设有另一个对象 B,使得 A.equals(B) 为真(并且它们的哈希值也相同),尽管 A 和 B 指向不同的对象.现在,当我检查对象 B 是否在哈希集 H 中时,它按预期返回 true。但是,假设现在,我想基于此访问对象 A 中的某些属性,然后我需要访问对象 A。请注意,访问 B 中的相同属性将不起作用,因为它们仅在 equals 方法下相等,但不指向同一个对象。我怎样才能做到这一点?

一种方法是使用 Hashmap,使得值类型与键类型相同,并且每次我在 hashmap 中存储一些键时,我都会存储与其值相同的对象。但这会导致存储值的额外内存开销,而我真正需要的是密钥本身的副本。有没有其他方法可以做到这一点?

【问题讨论】:

  • 所以如果你不能将 A 和 B 存储在同一个哈希集中,因为冲突。您打算如何访问它们?
  • @Vash:重点是根据B的值找到A的值。 B 值不需要存储在集合中......它只是用作查找键。不幸的是,Set 不允许您找到任何方法来查找“存储在集合中的值,恰好等于您提供的键值”。
  • @JonSkeet,这是一个强有力的假设。但在这种情况下,答案将是调用 map.get(objectB) 将返回 objectA. And that is why Map interface do not use generic type in get` 和 contains 方法的实例。
  • @JonSkeet,以及您在回答中所描述的内容。在医疗时间。不错。
  • @Vash:我不会说这是一个“强有力的假设”——这是对问题的一种解释,我认为这是一个非常合理的解释。问题中的所有内容都同意。

标签: java hashmap hashset


【解决方案1】:

我不相信使用HashSet<E> 可以有效地做到这一点。 (当然,您可以列出所有键并检查它们是否相等。)

然而,尽管HashMap 方法会烦人,但它实际上不会占用更多内存。 HashSet 是使用 HashMap 实现的(至少在股票 JDK 7 中)所以无论如何每个集合条目都有一个完整的映射条目......并且没有额外的内存来存储值,因为它们都只是引用同一个对象。

【讨论】:

  • 没关系,但至少也会存储引用本身的副本,对吗?我知道那是很小的内存,但为什么还要存储呢?
  • @ashu:HashSet已经存储了引用的副本 - 只是它是对 HashSet.PRESENT 的引用。这也不像存储非空引用比空引用占用更多空间:正如我所说,它在内部使用HashMap,因此从逻辑上讲,每个条目都有一个值,并且会有一个与之关联的字段值,而不管实际值是多少。所以没有没有更多的内存使用。
  • 是的,我刚刚明白了。谢谢!
【解决方案2】:

一种方法是使用 Hashmap,使得值类型与键类型相同,并且每次我在 hashmap 中存储一些键时,我都会存储与其值相同的对象。但这会导致存储值的额外内存开销,而我真正需要的是密钥本身的副本。

实际上,HashSet 在(至少)Java 7 中的 Oracle Java SE 库中的实现has a HashMap inside it。所以你担心HashMap 的额外内存使用是没有根据的。

这里是源代码链接:http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/7u40-b43/java/util/HashSet.java/

顺便说一下,内部映射声明为HashMap<E, Object> 而不是HashMap<E, E>。读者思考练习:他们为什么这样做?


唯一其他合理的选择(使用HashSet)是迭代集合元素,测试每个元素以查看它是否等于您正在寻找的元素。这显然是非常(时间)低效的。

【讨论】:

  • 我想不出思想实验部分的答案。您能对此提供一些见解吗?
  • @ashu - 这很微妙。当您在HashMap<E, E> 上使用get(...) 返回的值时,会将结果隐式 类型转换为E 类型。但在这种情况下,HashSet 只会测试该值是否为null,或者调用Object.equals。这些都不需要E。因此,通过改用Object,该实现避免了在某些Set 操作中不必要的类型转换。
  • 但我确实认为equals方法需要E
  • @ashu - 在编译时不需要调用。 (执行调用是必需的,但这是在运行时由普通的 Java 多态方法调度处理的。)
  • 好的,我现在明白了!
【解决方案3】:

您可以遍历 HashSet 中的所有值并检查 B 的 equals 方法。如果迭代器返回一个等于 B 的对象,则您找到了 A。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-07-05
    • 2023-03-18
    • 2012-06-14
    • 2021-05-20
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多