Java: Composite key in hashmaps

2020-02-17 09:02发布

I would like to store a group of objects in a hashmap , where the key shall be a composite of two string values. is there a way to achieve this?

i can simply concatenate the two strings , but im sure there is a better way to do this.

8条回答
劫难
2楼-- · 2020-02-17 09:35

I see that many people use nested maps. That is, to map Key1 -> Key2 -> Value (I use the computer science/ aka haskell curring notation for (Key1 x Key2) -> Value mapping which has two arguments and produces a value), you first supply the first key -- this returns you a (partial) map Key2 -> Value, which you unfold in the next step.

For instance,

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

I am not sure that it is better or not than the plain composite key construction. You may comment on that.

查看更多
该账号已被封号
3楼-- · 2020-02-17 09:36

You don't need to reinvent the wheel. Simply use the Guava's HashBasedTable<R,C,V> implementation of Table<R,C,V> interface, for your need. Here is an example

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

Happy coding!

查看更多
我只想做你的唯一
4楼-- · 2020-02-17 09:39

I have a similar case. All I do is concatenate the two strings separated by a tilde ( ~ ).

So when the client calls the service function to get the object from the map, it looks like this:

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

It is simple, but it works.

查看更多
ら.Afraid
5楼-- · 2020-02-17 09:40

Why not create a (say) Pair object, which contains the two strings as members, and then use this as the key ?

e.g.

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

   // this object should be immutable to reliably perform subsequent lookups
}

Don't forget about equals() and hashCode(). See this blog entry for more on HashMaps and keys, including a background on the immutability requirements. If your key isn't immutable, then you can change its components and a subsequent lookup will fail to locate it (this is why immutable objects such as String are good candidates for a key)

You're right that concatenation isn't ideal. For some circumstances it'll work, but it's often an unreliable and fragile solution (e.g. is AB/C a different key from A/BC ?).

查看更多
Summer. ? 凉城
6楼-- · 2020-02-17 09:48
public int hashCode() {
    return (str1 + str2).hashCode();
}

This seems to be a terrible way to generate the hashCode: Creating a new string instance every time the hash code is computed is terrible! (Even generating the string instance once and caching the result is poor practice.)

There are a lot of suggestions here:

How do I calculate a good hash code for a list of strings?

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

For a pair of strings, that becomes:

return string1.hashCode() * 31 + string2.hashCode();

That is a very basic implementation. Lots of advice through the link to suggest better tuned strategies.

查看更多
再贱就再见
7楼-- · 2020-02-17 09:50
public static String fakeMapKey(final String... arrayKey) {
    String[] keys = arrayKey;

    if (keys == null || keys.length == 0)
        return null;

    if (keys.length == 1)
        return keys[0];

    String key = "";
    for (int i = 0; i < keys.length; i++)
        key += "{" + i + "}" + (i == keys.length - 1 ? "" : "{" + keys.length + "}");

    keys = Arrays.copyOf(keys, keys.length + 1);

    keys[keys.length - 1] = FAKE_KEY_SEPARATOR;

    return  MessageFormat.format(key, (Object[]) keys);}
public static string FAKE_KEY_SEPARATOR = "~";

INPUT: fakeMapKey("keyPart1","keyPart2","keyPart3");
OUTPUT: keyPart1~keyPart2~keyPart3
查看更多
登录 后发表回答