如何使用两个号码作为地图的关键(How to use two numbers as a Map ke

2019-07-18 04:45发布

我有两个数字,我想一起使用它们作为一个关键Map 。 目前,我在他们的串联字符串表示。 例如,假设键号码4和12使用:

String key = 4 + "," + 12;

地图被声明为Map<String, Object>

我觉得这是如此糟糕! 我喜欢用比其他一些String作为关键! 我要创建这些键的最快方式。

谁拥有一个好主意?

Answer 1:

创建包含两个数字的对象,并将其作为重点。 例如:

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>();

如果你喜欢数学方法,请参阅该StackOverflow的答案 。



Answer 2:

您应该使用java.awt.Dimension中为你的关键。

尺寸键=新尺寸(4,12);

维具有产生针对每对正整数的一个不同的hashCode,以便为(4,12)和所述散列码(12,4)是不同的一个非常好的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

如果您按照散列码,以便从0到14,你会看到的模式。

下面是产生这种的hashCode的代码:

public int hashCode() {
    int sum = width + height;
    return sum * (sum + 1)/2 + width;
}

你可能认识到最后一行中的三角形数量的公式。 这就是为什么该表的第一列包含了所有的三角形数量。

对于速度,您应该计算在构造函数中的hashCode。 所以全班看起来是这样的:

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;
  }
}

我们限制此为正值,因为负值会产生一些重复的哈希码。 但随着这一限制,这些都是最快的hashCode()和equals()方法可以写入。 (当然,你可以通过在构造函数计算的hashCode写哈希码只在任何不可变类一样快。)

如果你不能与那些限制生活,你只需要保存参数。

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;
}

但这里的踢球。 你不需要这个类的。 由于公式为您提供的每一对数字的唯一整数,你可以只使用整数作为地图的关键。 Integer类都有自己的快速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;
}


Answer 3:

如果与对象的解决方案去, 请确保您的关键对象是不可改变的 。

否则,如果某人变异的值,不仅会不再是等于其他明显相同的值,但存储在地图的哈希码将不再匹配由返回的一个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)));
  }

打印:

true
true
false
false
false


Answer 4:

你可以存储两个整数在相当长的这个样子,

   long n = (l << 32) | (r & 0XFFFFFFFFL);

或者你可以使用下面Pair<Integer, Integer>类,

public class Pair<L, R> {

    private L l;
    private R r;

    public Pair() {
    }

    public Pair(L l, R r) {
        this.l = l;
        this.r = r;
    }

    public L getLeft() {
        return l;
    }

    public R getRight() {
        return r;
    }

    @Override
    public boolean equals(Object o) {
        if (!(o instanceof Pair)) {
            return false;
        }
        Pair obj = (Pair) o;
        return l.equals(obj.l) && r.equals(obj.r);
    }

    @Override
    public int hashCode() {
        return l.hashCode() ^ r.hashCode();
    }
} 


Answer 5:

一个实际的回答这个问题是:

hashCode = a + b * 17;

...其中a,b和hashCode都是整数。 17只是一个任意的素数。 你哈希不会是唯一的,不过没关系。 这种事情是各地使用Java标准库。



Answer 6:

另一种方法是使用嵌套的地图:

Map<Integer,Map<Integer,Object>>

在这里,你有没有开销创建密钥。 然而,你有更多的开销来正确地创建和检索条目,你总是需要来图来访问找到你正在寻找的对象。



Answer 7:

为什么要编写所有额外的代码,使一个完全成熟的类,你不需要为别的比使用一个简单的字符串比较好? 将计算的哈希代码该类的实例是比字符串快多少? 我不认为如此。

除非你是在一个非常有限的计算能力的环境中运行,使得和散列字符串不应该明显高于实例自定义类的大的开销。

我想以最快的方式是简单地在整数打包成一个单一的龙象ZZ编码器的建议,但在任何情况下,我不认为速度的收益是巨大的。



Answer 8:

你需要写正确eqauls和hashCode方法,或者产生一些错误。



文章来源: How to use two numbers as a Map key