【问题标题】:Accessing a HashSet using the HashCode directly? (Java)直接使用 HashCode 访问 HashSet? (爪哇)
【发布时间】:2015-01-15 04:26:42
【问题描述】:

您好,我想知道如果您拥有要查找的对象的哈希码,是否可以直接访问 HashSet 的内容,有点像使用 HashCode 作为 HashMap 中的键。

我想它可能会像这样工作:

MyObject object1 = new MyObject(1); 

Set<MyObject> MyHashSet = new HashSet<MyObject>();

MyHashSet.add(object1)

int hash = object1.getHashCode

MyObject object2 = MyHashSet[hash]???

谢谢!

编辑:感谢您的回答。好的,我知道我可能会稍微推动 HashSet 的合同,但是对于这个特定的项目,相等性完全由哈希码决定,我确信每个哈希码/哈希桶只有一个对象。我非常不愿意使用 HashMap 的原因是因为我需要将要映射的原始 int 转换为 Integer 对象,因为 HashMap 只接受对象作为键,而且我也担心这可能会影响性能。我还能做些什么来实现类似的东西吗?

【问题讨论】:

  • 不,这是不可能的。你为什么想做这个?好像你想要一张地图。
  • 没有这样的公共 API。即使有,它也可能在哈希码冲突时返回多个对象。
  • 无法以这种方式获取对象。这是有道理的,因为两个不同的对象可以具有相同的哈希码,但可能不相等。 Hashset 在内部使用 equals 方法比较两个对象,如果它们的哈希码匹配。
  • 好的,我知道我可能会稍微推动 HashSet 的合约,但是对于这个特定的项目,相等性完全由哈希码决定,我确信每个哈希码只有一个对象/哈希桶。我非常不愿意使用 HashMap 的原因是因为我需要将要映射的原始整数转换为 Integer 对象,因为 HashMap 仅将对象作为键,而且我也担心这可能会影响性能。我还能做些什么来实现类似的代码吗?
  • 这是一个线程,其中包含一些提供原始 int 键控的地图的库:stackoverflow.com/questions/16148575/hashmap-and-int-as-key

标签: java hash hashcode hashset


【解决方案1】:

HashSet 的常见实现由HashMap 支持(相当懒惰),因此您避免HashMap 的努力可能会失败。

基于过早优化是万恶之源,我建议您最初使用HashMap,如果intInteger 之间的装箱/拆箱开销确实是您将遇到的问题使用原始ints 实现(或查找)手工制作的HashSet 进行比较。 标准 Java 库真的不想关心装箱/拆箱成本。 很久以前,整个语言在简单性方面获得了相当大的收益,从而出售了该性能问题。 请注意,这些天(自 2004 年以来!)该语言会自动装箱和拆箱,这表明“您不必担心这个”政策。大多数情况下是对的。

我不知道你的HashKeyedSet 需要多么“丰富”,但是一个基本的哈希表真的不是太难。

【讨论】:

    【解决方案2】:

    HashSet 在内部由 HashMap 提供支持,不幸的是,该问题无法通过公共 API 获得。但是,我们可以使用反射来访问内部映射,然后找到具有相同hashCode 的键:

    private static <E> E getFromHashCode(final int hashcode, HashSet<E> set) throws Exception {
        // reflection stuff
        Field field = set.getClass().getDeclaredField("map");
        field.setAccessible(true);
    
        // get the internal map
        @SuppressWarnings("unchecked")
        Map<E, Object> interalMap = (Map<E, Object>) (field.get(set));
    
        // attempt to find a key with an identical hashcode
        for (E elem : interalMap.keySet()) {
            if (elem.hashCode() == hashcode) return elem;
        }
        return null;
    }
    

    用于示例:

    HashSet<String> set = new HashSet<>();
    set.add("foo"); set.add("bar"); set.add("qux");
    
    int hashcode = "qux".hashCode();
    
    System.out.println(getFromHashCode(hashcode, set));
    

    输出:

    qux
    

    【讨论】:

    • 谢谢!这是一个绝妙的方法,但是它需要使用 for 循环搜索 HashSet,我知道我现在要求的是不可能的,但我只是认为可能有一种方法可以直接获取该哈希码的数据,就像它是一个列表或其他东西。
    • 我怀疑有可能通过更多的反射技巧在恒定时间内执行此操作,因为HashMap 也不允许您通过公共 API 获取表中特定索引处的存储桶。跨度>
    【解决方案3】:

    这是不可能的,因为HashSet 是一个对象,并且没有这样的公共 API。多个对象也可以具有相同的哈希码,但对象可以不同。

    最后只能使用myArray[&lt;index&gt;] 语法访问数组。

    【讨论】:

      【解决方案4】:

      您可以轻松编写代码,使用反射直接访问 HashSet 实现的内部数据结构。当然,您的代码将取决于您正在编码的特定 JVM 的实现细节。您还将受到 SecurityManager(如果有)的约束。

      HashSet 的典型实现使用 HashMap 作为其内部数据结构。 HashMap 有一个数组,该数组由映射到数组索引的键的哈希码索引。通过在实现中调用非公共方法可以使用哈希码映射功能 - 您必须阅读源代码并弄清楚。找到正确的存储桶后,您只需要(使用equals)在存储桶中找到正确的条目。

      【讨论】:

        猜你喜欢
        • 2015-02-08
        • 2015-06-07
        • 1970-01-01
        • 2012-12-26
        • 1970-01-01
        • 2014-08-11
        • 1970-01-01
        • 1970-01-01
        • 2021-11-27
        相关资源
        最近更新 更多