Java HashMap冲突:我的代码正确吗?
我希望有一个DateWrapper—表示一个日期(为Hibernate持久性而构建,但这是另一个故事)—最多在同一时间存在于同一日期 我对冲突和哈希的好键有点困惑。我正在为一个Java HashMap冲突:我的代码正确吗?,java,hashmap,key,hash-collision,Java,Hashmap,Key,Hash Collision,我希望有一个DateWrapper—表示一个日期(为Hibernate持久性而构建,但这是另一个故事)—最多在同一时间存在于同一日期 我对冲突和哈希的好键有点困惑。我正在为一个DateWrapper对象编写一个工厂,我想使用解析日期的毫秒数作为键,就像我看到其他人所做的那样但是,如果发生碰撞会发生什么?。毫秒之间总是不同的,但是内部表可能小于可能存在的长度。一旦散列映射发生冲突,它就使用equals,但是它如何区分两个不同的对象和我的Long?也许,这是put方法来删除(覆盖)一些我想插入的值。
DateWrapper
对象编写一个工厂,我想使用解析日期的毫秒数作为键,就像我看到其他人所做的那样但是,如果发生碰撞会发生什么?。毫秒之间总是不同的,但是内部表可能小于可能存在的长度。一旦散列映射发生冲突,它就使用equals,但是它如何区分两个不同的对象和我的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;
}
}
包myproject.test;
导入java.util.HashMap;
导入java.util.Map;
导入org.joda.time.DateTime;
导入org.joda.time.format.DateTimeFormat;
导入org.joda.time.format.DateTimeFormatter;
导入myproject.utilities.DateWrapper;
公共类DateWrapperFactory{
静态映射缓存=新的HashMap();
静态日期时间格式分析器=
模式的日期时间格式(“yyyy-MM-dd”);
静态DateWrapperFactory实例=新建DateWrapperFactory();
私有DateWrapperFactory(){
}
公共静态DateWrapperFactory getInstance(){
返回实例;
}
公共静态日期包装器获取(字符串源){
DateTime d=parser.parseDateTime(源);
DateWrapper dw=cache.get(d.getMillis());
如果(dw!=null){
返回数据仓库;
}否则{
dw=新的日期包装器(d);
cache.put(d.getMillis(),dw);
返回数据仓库;
}
}
}
包myproject.test;
导入org.joda.time.DateTime;
公共类日期包装器{
私人日期时间日期;
公共日期包装器(DateTime dt){
这个日期=dt;
}
}
使用HashMap,您只能在任何给定键值下存储一个条目(例如,在您的情况下为Long)
另一方面,如果有并发的可能,您可能希望使用ConcurrentHashMap,而不是非原子的get/if/put调用。将在长键上调用
equals()
。您很好。如果您使用您想要的实际对象作为映射键,并让HashMap
处理这些对象的hashcode的细节(并且这些键根据它们的约定实现等于和hashcode
)如果存在哈希代码冲突,除了由于需要线性搜索散列到同一个bucket的每个条目而导致性能降低之外,不会有任何问题
在你的另一个问题中,碰撞的主题出现了一个问题,那就是你没有使用应该是键的实际对象,而是使用该对象的哈希代码作为键本身。这是不正确的,会导致不正确的行为。。。。当您在映射中查找给定键的值时,结果可能是实际映射到一个完全不同的键的值,该键恰好具有相同的哈希代码
这个故事的寓意是:使用实际的键或绝对等效的东西(如本例中的DateTime
的毫秒)作为键,而不是键的哈希码。HashMap
可以满足您对hashcode的需求。考虑到您最终要用它来完成的任务,这似乎不是非常有效。您有一个高度优化的数据结构,专门为快速搜索和强制唯一性而设计,称为数据库索引。从Hibernate开始,内存和二级缓存已经非常健壮。顺便说一句,它没有在静态字段上放置HashMap的线程安全问题
为什么不让这个数字成为数据库中ID列的值,让健壮的平台技术快速查找并缓存它呢?内存中二级缓存命中的速度实际上并不比总体方案中的HashMap慢多少。这将是一个非常罕见的应用程序,其中差异是您有意义的热点之一。抱歉,但地图使用的相等值是关键之一,不是吗?所以,我认为使用的不是DateWrapper的等价物,而是Long的等价物。事实上,这取决于你想要完成什么。听起来您选择的数据结构并不能满足您的需要。请更新您的问题,以表明您正在尝试做什么。完成(添加第一句)。我希望在同一时间内最多有一个DateWrappervalue@cdarwin:您也可以将DateTime
本身作为映射键,尽管我认为您所拥有的将起作用(当然,除非在多个线程上调用get
),你打算用它们做什么?为什么它们一开始就在一个散列中?@ColinD:那么他就必须自己实现equals()。现在还不清楚他到底想做什么,甚至HashMap是否是正确的数据结构。这是否相当于一个集合,通过getMillis()实现DateTime.equals?@extraneon:这不是等价的。这里的想法(我认为)是为每个使用的DateTime
实现一个规范/内部实例,这需要能够在不迭代的情况下获取该实例。。。这需要一张地图。好的,谢谢你,科林。我知道这一点,但混淆源于使用了一个键,该键对于您所引用的代码中的对象来说不是唯一的(对于其他读者,请参阅)。因此,它相当简单:hashmap中使用的键必须与一个且仅与一个对象对应,就像RDBMS中的主键一样