我们必须根据三个输入数据字段查找一些数据。查找必须快速。只有大约20种可能的查找组合。我们使用静态HashMap实例实现了这一点,我们通过连接三个数据字段来创建密钥。有没有更好的方法来做到这一点,或者这是要走的路?代码如下。
更新:我并不是说这段代码很慢。只是好奇是否有更好的方法来做到这一点。我认为可能会有一个更优雅的解决方案,但如果没有令人信服的选择,我很乐意保持这一点!
创建类级静态HashMap实例:
private static HashMap map = new HashMap();
我们如何将数据加载到内存中:
private void load(Iterator iterator) {
while (iterator.next()) {
Object o = it.next();
key = o.getField1() + "-" + o.getField2() + "-" o.getField3();
map.put(key, o.getData());
}
}
以及我们如何根据以下三个字段查找数据:
private Stirng getData(String f1, String f2, String f3) {
String key = f1 + "-" + f2 + "-" f3;
return map.get(key);
}
答案 0 :(得分:7)
嗯,问自己的问题当然是“它足够快吗?”因为除非你的应用程序需要更快,这是瓶颈,否则它无关紧要。你所拥有的已经相当有效。
话虽如此,如果你想从这个例程中挤出所有可能的速度(不用汇编语言重写它;-)你可能会考虑使用数组而不是HashMap
,因为只有一个小的,有限数量的键。您必须开发某种散列函数,将每个对象散列为0到19之间的唯一数字(或者实际上有多少元素)。您也可以优化该哈希函数的实现,但我不知道如何在不知道您正在使用的对象的详细信息的情况下如何做到这一点。
答案 1 :(得分:3)
您可以创建一个具有三个String字段的特殊键对象,以避免构建键字符串:
class MapKey {
public final String k1;
public final String k2;
public final String k3;
public MapKey(String k1, String k2, String k3) {
this.k1 = k1; this.k2 = k2; this.k3 = k3;
}
public MapKey(Object o) {
this.k1 = o.getField1(); this.k2 = o.getField2(); this.k3 = o.getField3();
}
public int hashCode() {
return k1.hashCode(); // if k1 is likely to be the same, also add hashes from k2 and k3
}
}
答案 2 :(得分:1)
在您的情况下,我将继续使用您概述的实施。对于映射到常量数据的大量常量键,可以使用Minimal Perfect Hashing。由于编写这个代码并不容易,而且我不确定现有的库,因此在使用它之前必须考虑实现成本。
答案 3 :(得分:1)
我认为你的方法非常快。通过实现自己的散列算法获得的任何收益都非常小,特别是与所需的努力相比。
关于您的密钥格式的一句话。最好确保分隔符不能出现在字段toString()值中,否则可能会发生关键冲突:
field1="a-", field2="b-", field3="c" -> key="a--b--c"
field1="a", field2="-b", field3="-c" -> key="a--b--c"
答案 4 :(得分:1)
连接字符串是创建密钥的一个坏主意。我的主要目标是不清楚。但实际上很大一部分实现都存在错误,特别是分隔符实际上可能出现在字符串中。在性能方面,我看到只需将字符串黑客的密钥更改为有意义的密钥对象,程序速度就会提高10%。 (如果您真的必须对代码保持懒惰,可以使用Arrays.asList
制作密钥 - 请参阅List.equals
API文档。)
答案 5 :(得分:0)
另一种完成此操作的方法是创建一个Object
来处理您的密钥,您可以使用该密钥覆盖equals()
(和hashCode()
)来对一个密钥进行测试,依次测试field1
,field2
和field3
。
编辑(回应评论):
由于地图使用hashCode()
返回的值将您的密钥放入存储桶(然后将从中测试equals
),因此理论上所有密钥的值都相同。但是,我不建议这样做,因为你不会从HashMaps性能中获益。您基本上会迭代存储桶中的所有项目并测试equals()
。
您可以采取的一种方法是将对hashCode()
的调用委托给密钥容器中的一个值。例如,您总是可以从field3
返回hashCode。在这种情况下,您可以将密钥分发给可能与field3
的不同值一样多的存储桶。一旦您的HashMap
找到了存储桶,它仍然需要迭代存储桶中的项目以测试equals()
的结果,直到找到匹配为止。
您可以创建的是hashCode()
在所有字段上返回的值的总和。如上所述,此值不需要是唯一的。此外,碰撞的可能性以及因此更大的铲斗的可能性要小得多。考虑到这一点,您在HashMap
上的查找应该更快。
EDIT2:
此密钥的良好哈希码问题已在单独的问题here
中得到解答答案 6 :(得分:0)
由于您只有20种组合,因此在了解每种组合的特征的基础上手工制作“给我这个组合的指数1..20”是可行的。
您是否能够列出确切的组合列表?