Java 如何使用两个数字作为映射键
我有两个数字,我想将它们一起用作Java 如何使用两个数字作为映射键,java,performance,data-structures,collections,key,Java,Performance,Data Structures,Collections,Key,我有两个数字,我想将它们一起用作地图中的键。目前,我正在连接它们的字符串表示。例如,假设键号是4和12。我使用: String key = 4 + "," + 12; 该映射被声明为map 我觉得这太糟糕了!我喜欢用字符串以外的东西作为键!我希望以最快的方式创建这些关键点 谁有好主意 创建一个包含这两个数字的对象,并将其用作键。例如: class Coordinates { private int x; private int y; public Coordinates(int
地图中的键。目前,我正在连接它们的字符串表示。例如,假设键号是4和12。我使用:
String key = 4 + "," + 12;
该映射被声明为map
我觉得这太糟糕了!我喜欢用字符串以外的东西作为键!我希望以最快的方式创建这些关键点
谁有好主意 创建一个包含这两个数字的对象,并将其用作键。例如:
class Coordinates {
private int x;
private int y;
public Coordinates(int x, int y) {
...
}
// getters
// equals and hashcode using x and y
}
Map<Coordinates, Location> locations = new HashMap<Coordinates, Location>();
类坐标{
私人INTX;
私营企业;
公共坐标(整数x,整数y){
...
}
//吸气剂
//使用x和y的equals和hashcode
}
映射位置=新的HashMap();
如果您更喜欢数学方法,请参见。您需要编写正确的eqauls和hashcode方法,否则会产生一些错误。您可以在这样的长格式中存储两个整数
long n = (l << 32) | (r & 0XFFFFFFFFL);
long n=(l如果使用对象解决方案,请确保键对象是不可变的
否则,如果有人对该值进行了变异,不仅它将不再等于其他明显相同的值,而且存储在映射中的hashcode将不再与hashcode()
方法返回的值匹配。此时,您基本上是SOL
例如,使用java.awt.Point
——从纸面上看,它与您想要的完全一样——如下所示:
public static void main(String[] args) {
Map<Point, Object> map = new HashMap<Point, Object>();
Point key = new Point(1, 3);
Object val = new Object();
map.put(key, val);
System.out.println(map.containsKey(key));
System.out.println(map.containsKey(new Point(1, 3)));
// equivalent to setLeft() / setRight() in ZZCoder's solution,
// or setX() / setY() in SingleShot's
key.setLocation(2, 4);
System.out.println(map.containsKey(key));
System.out.println(map.containsKey(new Point(2, 4)));
System.out.println(map.containsKey(new Point(1, 3)));
}
另一种方法是使用嵌套贴图:
Map<Integer,Map<Integer,Object>>
Map
在这里,您没有创建键的开销。但是,正确创建和检索条目的开销更大,并且您需要始终映射访问以查找您要查找的对象。为什么编写所有这些额外的代码来创建一个完全成熟的类,而您不需要其他任何东西,这比使用一个简单的字符串更好呢为该类的实例计算哈希代码比为字符串计算哈希代码要快得多?我不这么认为
除非您运行在一个计算能力极其有限的环境中,否则生成和散列字符串的开销不应该明显大于实例化自定义类的开销
我想最快的方法是按照ZZ编码器的建议,将整数打包成一个长整数,但无论如何,我不认为速度会有实质性的提高。这个问题的一个实际答案是:
hashCode = a + b * 17;
…其中a、b和hashCode都是整数。17只是一个任意素数。您的哈希值不会唯一,但这没关系。这种情况在Java标准库中随处可见。您应该使用Java.awt.Dimension作为密钥
尺寸键=新尺寸(4,12)
Dimension有一个非常好的hashCode()方法,它为每对正整数生成不同的hashCode,因此(4,12)和(12,4)的hashCode是不同的。因此,这些方法可以快速实例化并生成非常好的hashCode
我真希望他们能使类不可变,但您可以使自己的不可变类基于维度建模
下面的表格显示了不同宽度和高度值的哈希代码:
0 1 2 3 4 <-- width
+--------------------
0 | 0 2 5 9 14
1 | 1 4 8 13
2 | 3 7 12
3 | 6 11
4 | 10
^
|
height
你可以在最后一行中认出三角形数字的公式,这就是为什么表格的第一列包含所有三角形数字的原因
为了提高速度,您应该在构造函数中计算哈希代码。因此,您的整个类可以如下所示:
public class PairHash {
private final int hash;
public PairHash(int a, int b) {
int sum = a+b;
hash = sum * (sum+1)/2 + a;
}
public int hashCode() { return hash; }
}
当然,如果您可能需要一个equals方法,但您将自己限制为不会溢出的正整数,则可以添加一个非常快速的方法:
public class PairHash {
// PAIR_LIMIT is 23170
// Keeping the inputs below this level prevents overflow, and guarantees
// the hash will be unique for each pair of positive integers. This
// lets you use the hashCode in the equals method.
public static final int PAIR_LIMIT = (int) (Math.sqrt(Integer.MAX_VALUE))/2;
private final int hash;
public PairHash(int a, int b) {
assert a >= 0;
assert b >= 0;
assert a < PAIR_LIMIT;
assert b < PAIR_LIMIT;
int sum = a + b;
hash = sum * (sum + 1) / 2 + a;
}
public int hashCode() { return hash; }
public boolean equals(Object other) {
if (other instanceof PairHash){
return hash == ((PairHash) other).hash;
}
return false;
}
}
但关键是。你根本不需要这个类。因为公式为每对数字提供了一个唯一的整数,所以你可以使用这个整数作为映射键。integer类有自己的fast equals()和hashCode方法。此方法将从两个短值生成哈希键。限制是您的输入必须是正的短值。这保证不会溢出,并且通过将中间和强制转换为长值,它比以前的方法具有更大的范围:它适用于所有正的短值
static int hashKeyFromPair(short a, short b) {
assert a >= 0;
assert b >= 0;
long sum = (long) a + (long) b;
return (int) (sum * (sum + 1) / 2) + a;
}
谢谢,你的方法很好,但我不想用“类”来解决这个问题。如何使用普通的数学方法来获得一把钥匙?我只是问,“最快的”好的。我已经添加了一个链接到你想要的答案:-)好的-所以这现在很明显是一个家庭作业问题。这里正确的解决方案是使用类。该类的hashcode()方法的实现是性能发挥作用的地方。如果将其用作HashMap键,您真的希望x和y是可变的吗?此外,如果您需要从映射中查找内容,而不仅仅是对其进行迭代,您应该重写hashcode()和equals()方法。这将允许您:在将来创建坐标对象,并从已填充的地图中检索内容。否则,即使创建的坐标与地图中用作键的坐标具有相同的x和y,也不会返回该值。B这将缩短MapI中的查找时间。我认为用逗号分隔的字符串是个好主意。我一直都在使用这种方法。我会对漂亮的int->long解决方案投赞成票,但我也会对使对
可变投反对票。不能将原语用作泛型参数。这是一个很好的技巧,但一旦这两个整数存储在那里,我就很难从中提取出它们。您可以使用什么位操作来提取这两个整数(r和l)?(1,2)和(2,1)的哈希代码相同。对于好奇的人,此答案中使用的函数称为。
public class PairHash {
private final int hash;
public PairHash(int a, int b) {
int sum = a+b;
hash = sum * (sum+1)/2 + a;
}
public int hashCode() { return hash; }
}
public class PairHash {
// PAIR_LIMIT is 23170
// Keeping the inputs below this level prevents overflow, and guarantees
// the hash will be unique for each pair of positive integers. This
// lets you use the hashCode in the equals method.
public static final int PAIR_LIMIT = (int) (Math.sqrt(Integer.MAX_VALUE))/2;
private final int hash;
public PairHash(int a, int b) {
assert a >= 0;
assert b >= 0;
assert a < PAIR_LIMIT;
assert b < PAIR_LIMIT;
int sum = a + b;
hash = sum * (sum + 1) / 2 + a;
}
public int hashCode() { return hash; }
public boolean equals(Object other) {
if (other instanceof PairHash){
return hash == ((PairHash) other).hash;
}
return false;
}
}
public class PairHash {
private final int a, b, hash;
public PairHash(int a, int b) {
this.a = a;
this.b = b;
int sum = a+b;
hash = sum * (sum+1)/2 + a;
}
public int hashCode() { return hash; }
public boolean equals(Object other) {
if (other instanceof PairHash) {
PairHash otherPair = (PairHash)other;
return a == otherPair.a && b == otherPair.b;
}
return false;
}
static int hashKeyFromPair(short a, short b) {
assert a >= 0;
assert b >= 0;
long sum = (long) a + (long) b;
return (int) (sum * (sum + 1) / 2) + a;
}