如何从 3 个整数组成 HashMap 的键?
How to compose a key for a HashMap from 3 integers?
我需要一个从 3 个整数到一个对象的唯一映射。我不能对这些整数的值做任何假设,除了它们是正数。
我目前使用这样的哈希图来实现它:
int a = 10;
int b = 20;
int c = 30;
Map<String, MyObject> map = new HashMap<>();
map.put(a + "_" + b + "_" + c, myObject);
虽然这行得通,但看起来有点丑。
有没有更好的选择,既不引入冲突,又不使用第 3 方库?
编辑:我刚刚意识到我实际上可以对我的整数做出更具体的假设。它们都在 1,000,000 到 2,000,000 之间,权重在底部,因为它们是从 1,000,000 开始计数的序列。
final class Key{
int a , b , c;
public Key(int a , int b , int c){
this.a = a;
this.b = b;
this.c = c;
}
public int hashCode(){
return (a << 10 ^ b << 5 ^ c);
}
public boolean equals(Object o){
if(!(o instanceof Key))
return false;
Key k = (Key) o;
return (k.a == a && k.b == b && k.c == c);
}
}
即使是负值也应该有效。
您可以像这样创建 Key
class:
final class Key {
final int a;
final int b;
final int c;
Key(int a, int b, int c) {
this.a = a;
this.b = b;
this.c = c;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (!(obj instanceof Key)) {
return false;
}
Key that = (Key) obj;
return (this.a == that.a)
&& (this.b == that.b)
&& (this.c == that.c);
}
@Override
public int hashCode() {
return Objects.hash(this.a, this.b, this.c);
}
}
并使您的地图成为 Map<Key, MyObject>
。
与使用 String
相比,它的一个优点是类型安全。您将无法执行此操作,例如:
map.put("Some arbitrary string that has nothing to do with numbers", myObject);
正如某些答案所指出的那样,拥有自己的密钥 class 就可以了。像往常一样使用 hashcode 和 equals 检测冲突。
就个人而言,我喜欢选择无限的整数数组以提高灵活性。
public final class IntArrayKey{
private final List<Integer> values = new ArrayList<>();
private final int hash;
private final String stringValue;
public IntArrayKey(Integer ...ints){
values.addAll(Arrays.asList(ints));
hash = Arrays.hashCode(ints);
stringValue = values.toString();
}
@Override
public boolean equals(Object o){
if(o == this) return true;
if(!(o instanceof IntArrayKey)) return false;
return values.equals(((IntArrayKey) o).values);
}
@Override
public int hashCode(){
return hash;
}
@Override
public String toString(){
return stringValue;
}
}
因为每个值的范围最多有2M个不同的连续值,即在221个值以下,并且有3个这样的值,不同组合的数量是低于 263。如此多的组合正好(刚好)适合 long
值的范围 (264),因此通过正确的数学运算,a、b 和 c 的每个唯一组合都可以有其拥有 long
价值。
这是一个简洁且高性能的工具,它使用位操作来完成工作:
// safe for a, b and c in range 0-2,097,151 (21 bits)
private static long key(long a, long b, long c) {
return a << 42 | b << 21 | c;
}
如果您将地图的键类型设置为Long
,那么您可以使用此方法的结果作为键:
Map<Long, MyObject> map = new HashMap<>();
map.put(key(a, b, c), myObject);
我还没有测量 key()
方法的性能,但只有几个 CPU 周期。
我需要一个从 3 个整数到一个对象的唯一映射。我不能对这些整数的值做任何假设,除了它们是正数。
我目前使用这样的哈希图来实现它:
int a = 10;
int b = 20;
int c = 30;
Map<String, MyObject> map = new HashMap<>();
map.put(a + "_" + b + "_" + c, myObject);
虽然这行得通,但看起来有点丑。
有没有更好的选择,既不引入冲突,又不使用第 3 方库?
编辑:我刚刚意识到我实际上可以对我的整数做出更具体的假设。它们都在 1,000,000 到 2,000,000 之间,权重在底部,因为它们是从 1,000,000 开始计数的序列。
final class Key{
int a , b , c;
public Key(int a , int b , int c){
this.a = a;
this.b = b;
this.c = c;
}
public int hashCode(){
return (a << 10 ^ b << 5 ^ c);
}
public boolean equals(Object o){
if(!(o instanceof Key))
return false;
Key k = (Key) o;
return (k.a == a && k.b == b && k.c == c);
}
}
即使是负值也应该有效。
您可以像这样创建 Key
class:
final class Key {
final int a;
final int b;
final int c;
Key(int a, int b, int c) {
this.a = a;
this.b = b;
this.c = c;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (!(obj instanceof Key)) {
return false;
}
Key that = (Key) obj;
return (this.a == that.a)
&& (this.b == that.b)
&& (this.c == that.c);
}
@Override
public int hashCode() {
return Objects.hash(this.a, this.b, this.c);
}
}
并使您的地图成为 Map<Key, MyObject>
。
与使用 String
相比,它的一个优点是类型安全。您将无法执行此操作,例如:
map.put("Some arbitrary string that has nothing to do with numbers", myObject);
正如某些答案所指出的那样,拥有自己的密钥 class 就可以了。像往常一样使用 hashcode 和 equals 检测冲突。
就个人而言,我喜欢选择无限的整数数组以提高灵活性。
public final class IntArrayKey{
private final List<Integer> values = new ArrayList<>();
private final int hash;
private final String stringValue;
public IntArrayKey(Integer ...ints){
values.addAll(Arrays.asList(ints));
hash = Arrays.hashCode(ints);
stringValue = values.toString();
}
@Override
public boolean equals(Object o){
if(o == this) return true;
if(!(o instanceof IntArrayKey)) return false;
return values.equals(((IntArrayKey) o).values);
}
@Override
public int hashCode(){
return hash;
}
@Override
public String toString(){
return stringValue;
}
}
因为每个值的范围最多有2M个不同的连续值,即在221个值以下,并且有3个这样的值,不同组合的数量是低于 263。如此多的组合正好(刚好)适合 long
值的范围 (264),因此通过正确的数学运算,a、b 和 c 的每个唯一组合都可以有其拥有 long
价值。
这是一个简洁且高性能的工具,它使用位操作来完成工作:
// safe for a, b and c in range 0-2,097,151 (21 bits)
private static long key(long a, long b, long c) {
return a << 42 | b << 21 | c;
}
如果您将地图的键类型设置为Long
,那么您可以使用此方法的结果作为键:
Map<Long, MyObject> map = new HashMap<>();
map.put(key(a, b, c), myObject);
我还没有测量 key()
方法的性能,但只有几个 CPU 周期。