【问题标题】:HashMap collisions: is my code correct?HashMap 冲突:我的代码正确吗?
【发布时间】:2010-11-15 19:22:59
【问题描述】:

我想要一个 DateWrapper - 代表一个日期(为 Hibernate 持久性而构建,但这是另一回事) - 最多同时存在于同一日期。

我对冲突和散列的好键有点困惑。我正在为DateWrapper 对象编写一个工厂,我想使用解析日期的毫秒数作为我看到其他人所做的关键。 但是,如果发生碰撞会怎样?。毫秒总是彼此不同,但内部表可能小于可能存在的 Long。并且一旦哈希映射发生冲突,它就会使用等号,但是它如何区分两个不同的对象和我的 Long 呢?也许,这是删除(覆盖)我想插入的一些值的 put 方法...... 那么,这段代码是安全的,还是被窃听了?

package myproject.test;

import java.util.HashMap;
import java.util.Map;

import org.joda.time.DateTime;
import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.DateTimeFormatter;

import myproject.utilities.DateWrapper;

public class DateWrapperFactory {

    static Map <Long, DateWrapper> cache = new HashMap<Long, DateWrapper>();
    static DateTimeFormatter parser =
        DateTimeFormat.forPattern("yyyy-MM-dd");

    static DateWrapperFactory instance = new DateWrapperFactory();

    private DateWrapperFactory() {
    }

    public static DateWrapperFactory getInstance() {
        return instance;
    }


    public static DateWrapper get(String source) {
        DateTime d = parser.parseDateTime(source);
        DateWrapper dw = cache.get(d.getMillis());
        if (dw != null) {
            return dw;
        } else {
            dw = new DateWrapper(d);
            cache.put(d.getMillis(), dw);
            return dw;
        }
    }

}

package myproject.test;

import org.joda.time.DateTime;

public class DateWrapper {

    private DateTime date;

    public DateWrapper(DateTime dt) {
        this.date = dt;
    }

}

【问题讨论】:

  • 这不就相当于一个Set,通过getMillis()实现DateTime.equals吗?
  • @extraneon:这不等同。这里的想法(我认为)是为正在使用的每个 DateTime 实现一个单一的规范/实习实例,这需要能够在不迭代的情况下获取该实例......这需要一个地图。

标签: java hashmap key hash-collision


【解决方案1】:

如果您使用您想要的实际对象作为映射键,并让HashMap 负责处理这些对象的哈希码的详细信息(并且键实现equalshashCode 根据他们的合同)如果存在哈希码冲突,除了由于需要线性搜索散列到同一存储桶的每个条目而可能降低性能之外,不会有任何问题。

在您的另一个问题中出现冲突主题的问题是,您没有使用应该是键的实际对象,而是使用该对象的哈希码作为键本身。这是不正确的,并且会导致不正确的行为....当您在映射中查找给定键的值时,结果可能是实际映射到恰好具有的完全不同键的值相同的哈希码。

故事的寓意是:使用 实际密钥 或绝对等价的东西(如本例中的 DateTime 的毫秒)作为密钥,而不是密钥的哈希码。 HashMap 使用哈希码为您完成所需的工作。

【讨论】:

  • 好的,谢谢科林。我知道这一点,但混淆来自使用的密钥对于您所指的代码中的对象不是唯一的(对于其他读者,请参阅stackoverflow.com/questions/4179641/…)。因此,它相当简单:hashmap 中使用的键必须对应一个且只有一个对象,就像 RDBMS 中的主键一样
【解决方案2】:

equals() 将在长键上调用,而不是值。你很好。

【讨论】:

  • 实际上,这取决于您要完成的工作。听起来您选择的数据结构不会做您想做的事。请更新您的问题以表明您正在尝试做什么。
  • 完成(添加第一句)。对于相同的 DateTime 值,我希望最多同时存在一个 DateWrapper
  • @cdarwin:您不妨将DateTime 本身设置为映射键,尽管我认为您所拥有的会起作用(当然,除非get 可以在多个线程上调用)。
  • 一旦您在哈希中拥有所有这些日期包装器,您将如何处理它们?为什么它们一开始就在哈希中?
  • @ColinD:那么他必须自己实现 equals() 。尚不清楚他真正想要做什么,甚至 HashMap 是否是正确的数据结构。
【解决方案3】:

使用 HashMap,您只能在任何给定键值下存储一个条目(例如,在您的情况下为 Long)。

附带说明,如果有任何并发​​的机会,您可能希望使用 ConcurrentHashMap 和 putIfAbsent() 而不是非原子的 get/if/put 调用。

【讨论】:

  • 抱歉,地图使用的等号是关键之一,不是吗?所以,我认为这不是 DateWrapper 的等号,而是 Long 的等号。
【解决方案4】:

考虑到您最终要通过此实现的目标,这似乎并没有太大的成效。您有一个高度优化的数据结构,专为快速搜索和强制唯一性而设计,称为数据库索引。 Hibernate 已经为您提供了极其强大的内存和 L2 缓存。顺便说一句,没有将 HashMap 放在静态字段上的线程安全问题。

为什么不将该数字作为数据库中 ID 列的值,让强大的平台技术负责快速找到它并为您缓存它?内存中的 L2 缓存命中实际上并不比总体方案中的 HashMap 慢多少。这将是一个非常罕见的应用程序,其中差异是您有意义的热点之一。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-12-23
    • 2019-02-23
    • 1970-01-01
    • 2016-05-26
    • 1970-01-01
    • 2020-11-06
    相关资源
    最近更新 更多