Java:哈希映射中的复合键

use*_*861 25 java collections hash hashmap

我想在一个hashmap中存储一组对象,其中key应该是两个字符串值的组合.有没有办法实现这个目标?

我可以简单地连接两个字符串,但我确定有更好的方法来做到这一点.

Tud*_*dor 41

您可以拥有一个包含两个字符串的自定义对象:

class StringKey {
    private String str1;
    private String str2;
}
Run Code Online (Sandbox Code Playgroud)

问题是,您需要确定两个此类对象的相等性测试和哈希码.

平等可以是两个字符串上的匹配,并且哈希码可以是连接成员的哈希码(这是有争议的):

class StringKey {
    private String str1;
    private String str2;

    @Override
    public boolean equals(Object obj) {
        if(obj != null && obj instanceof StringKey) {
            StringKey s = (StringKey)obj;
            return str1.equals(s.str1) && str2.equals(s.str2);
        }
        return false;
    }

    @Override
    public int hashCode() {
        return (str1 + str2).hashCode();
    }
}
Run Code Online (Sandbox Code Playgroud)

  • ABC、D 和 AB、CD 的哈希码相同是否存在问题?或者平等不同可以解决这个问题吗? (2认同)

Tho*_*nti 10

public int hashCode() {
    return (str1 + str2).hashCode();
}
Run Code Online (Sandbox Code Playgroud)

这似乎是一种生成hashCode的可怕方法:每次计算哈希码时创建一个新的字符串实例都很糟糕!(即使生成一次字符串实例并缓存结果也是不好的做法.)

这里有很多建议:

如何计算字符串列表的良好哈希码?

public int hashCode() {
    final int prime = 31;
    int result = 1;
    for ( String s : strings ) {
        result = result * prime + s.hashCode();
    }
    return result;
}
Run Code Online (Sandbox Code Playgroud)

对于一对字符串,它变为:

return string1.hashCode() * 31 + string2.hashCode();
Run Code Online (Sandbox Code Playgroud)

这是一个非常基本的实现.通过链接提供了大量建议,以提出更好的调整策略.


Bri*_*new 7

为什么不创建一个(说)Pair对象,其中包含两个字符串作为成员,然后使用它作为键?

例如

public class Pair {
   private final String str1;
   private final String str2;

   // this object should be immutable to reliably perform subsequent lookups
}
Run Code Online (Sandbox Code Playgroud)

不要忘记equals()hashCode().有关HashMaps和密钥的更多信息,请参阅此博客条目,其中包括有关不变性要求的背景知识.如果您的密钥不是不可变的,那么您可以更改其组件,并且后续查找将无法找到它(这就是为什么不可变对象,如String密钥的良好候选者)

你认为串联并不理想是正确的.在某些情况下它会起作用,但它通常是一个不可靠和脆弱的解决方案(例如AB/C是否A/BC不同?).


Edg*_*ase 5

我有一个类似的情况。我要做的是将由波浪号(〜)分隔的两个字符串连接起来。

因此,当客户端调用服务函数从地图中获取对象时,它看起来像这样:

MyObject getMyObject(String key1, String key2) {
    String cacheKey = key1 + "~" + key2;
    return map.get(cachekey);
}
Run Code Online (Sandbox Code Playgroud)

这很简单,但是有效。


小智 5

我看到很多人使用嵌套映射。也就是说,要映射Key1 -> Key2 -> Value(我使用计算机科学/又名 haskell curring 符号进行(Key1 x Key2) -> Value映射,它有两个参数并产生一个值),首先提供第一个键——这会返回一个(部分)映射 Key2 -> Value,你在下一步。

例如,

Map<File, Map<Integer, String>> table = new HashMap(); // maps (File, Int) -> Distance

add(k1, k2, value) {
  table2 = table1.get(k1);
  if (table2 == null) table2 = table1.add(k1, new HashMap())
  table2.add(k2, value)
}

get(k1, k2) {
  table2 = table1.get(k1);
  return table2.get(k2)
}
Run Code Online (Sandbox Code Playgroud)

我不确定它是否比普通的复合键结构更好。你可以对此发表评论。


TMt*_*ech 5

您不需要重新发明轮子。使用时只需将番石榴HashBasedTable<R,C,V>执行Table<R,C,V>接口,为您的需要。这是一个例子

Table<String, String, Integer> table = HashBasedTable.create();

table.put("key-1", "lock-1", 50);
table.put("lock-1", "key-1", 100);

System.out.println(table.get("key-1", "lock-1")); //prints 50
System.out.println(table.get("lock-1", "key-1")); //prints 100

table.put("key-1", "lock-1", 150); //replaces 50 with 150
Run Code Online (Sandbox Code Playgroud)

编码愉快!