如何使用多个键实现Map?

iva*_*off 144 java data-structures

我需要一个行为类似于Map的数据结构,但使用多个(不同类型的)键来访问其值.
(我们不要太笼统,让我们说两把钥匙)

钥匙保证是独一无二的.

就像是:

MyMap<K1,K2,V> ...
Run Code Online (Sandbox Code Playgroud)

使用以下方法:

getByKey1(K1 key)...
getByKey2(K2 key)...
containsKey1(K1 key)...
containsKey2(K2 key)...
Run Code Online (Sandbox Code Playgroud)

你有什么建议吗?

我唯一能想到的是:
编写一个内部使用两个Maps的类.

编辑 有些人建议我使用元组,或类似作为Java Map的键,但这对我不起作用:
如上所述,我必须能够通过两个键中的一个来搜索值指定.
地图使用密钥的哈希码并检查它们的相等性.

Jer*_*amp 96

两张地图.一个Map<K1, V>和一个Map<K2, V>.如果必须有单个接口,请编写实现所述方法的包装类.

  • 呃,是的,这正是你提出的.两张地图是要走的路.要扩展到任意数量的键,请使用getByKey(MetaKey mk,Object k)之类的方法,然后在内部使用Map <MetaKey,Map <Object,V >>. (7认同)

Nat*_*ger 47

Commons-collections提供您正在寻找的内容:https: //commons.apache.org/proper/commons-collections/apidocs/

看起来现在是commons-collections的类型.

可以在以下网址找到打字版本:https: //github.com/megamattron/collections-generic

这将完全支持您的用例:

 MultiKeyMap<k1,k2,...,kn,v> multiMap = ??
Run Code Online (Sandbox Code Playgroud)

  • 我认为这是不对的.没有办法只使用第二个键从公共MultiKeyMap获取值.您始终需要同时指定key1和key2.为什么这个答案获得了如此多的票数? (57认同)
  • @Dime它得到了很多赞成,因为它是一个非常有用的答案,因为许多人(像我一样)最终在这个问题上找到答案.这只是一个错误的问题;) (9认同)
  • @Dime 我误解了问题和答案,但最终得到了我需要的东西:) (3认同)

Log*_*ldo 40

我仍然会建议2地图解决方案,但有一个推特

Map<K2, K1> m2;
Map<K1, V>  m1;
Run Code Online (Sandbox Code Playgroud)

此方案允许您拥有任意数量的键"别名".

它还允许您通过任何键更新值,而不会使地图不同步.

  • 唯一的问题是如果你没有K1. (18认同)

Tom*_*art 33

另一种解决方案是使用谷歌的番石榴

import com.google.common.collect.Table;
import com.google.common.collect.HashBasedTable;

Table<String, String, Integer> table = HashBasedTable.create();
Run Code Online (Sandbox Code Playgroud)

用法非常简单:

String row = "a";
String column = "b";
int value = 1;

if (!table.contains(row, column)) {
    table.put(row, column, value);
}

System.out.println("value = " + table.get(row, column));
Run Code Online (Sandbox Code Playgroud)

该方法HashBasedTable.create()基本上是这样做的:

Table<String, String, Integer> table = Tables.newCustomTable(
        Maps.<String, Map<String, Integer>>newHashMap(),
        new Supplier<Map<String, Integer>>() {
    public Map<String, Integer> get() {
        return Maps.newHashMap();
    }
});
Run Code Online (Sandbox Code Playgroud)

如果你想创建一些自定义地图,你应该选择第二个选项(如@Karatheodory建议的那样),否则你应该对第一个选项没问题.

  • 在上面的例子中,对于基于哈希的表,最好使用 Guava 的默认工厂:`HashBasedTable.create()`。`newCustomTable()` 方法应该只用于真正的自定义地图。 (2认同)

小智 16

那么你宣布以下"Key"类:

public class Key {
   public Object key1, key2, ..., keyN;

   public Key(Object key1, Object key2, ..., Object keyN) {
      this.key1 = key1;
      this.key2 = key2;
      ...
      this.keyN = keyN;
   }

   @Override   
   public boolean equals(Object obj) {
      if (!(obj instanceof Key))
        return false;
      Key ref = (Key) obj;
      return this.key1.equals(ref.key1) && 
          this.key2.equals(ref.key2) &&
          ...
          this.keyN.equals(ref.keyN)
   }

    @Override
    public int hashCode() {
        return key1.hashCode() ^ key2.hashCode() ^ 
            ... ^ keyN.hashCode();
    }

}
Run Code Online (Sandbox Code Playgroud)

声明地图

Map<Key, Double> map = new HashMap<Key,Double>();
Run Code Online (Sandbox Code Playgroud)

声明关键对象

Key key = new Key(key1, key2, ..., keyN)
Run Code Online (Sandbox Code Playgroud)

填写地图

map.put(key, new Double(0))
Run Code Online (Sandbox Code Playgroud)

从地图中获取对象

Double result = map.get(key);
Run Code Online (Sandbox Code Playgroud)

  • OP 不想组合键,而是单独使用。 (3认同)

iva*_*off 5

提案,正如一些答复者所建议的那样:

public interface IDualMap<K1, K2, V> {

    /**
    * @return Unmodifiable version of underlying map1
    */
    Map<K1, V> getMap1();

    /**
    * @return Unmodifiable version of underlying map2
    */
    Map<K2, V> getMap2();

    void put(K1 key1, K2 key2, V value);

}

public final class DualMap<K1, K2, V>
        implements IDualMap<K1, K2, V> {

    private final Map<K1, V> map1 = new HashMap<K1, V>();

    private final Map<K2, V> map2 = new HashMap<K2, V>();

    @Override
    public Map<K1, V> getMap1() {
        return Collections.unmodifiableMap(map1);
    }

    @Override
    public Map<K2, V> getMap2() {
        return Collections.unmodifiableMap(map2);
    }

    @Override
    public void put(K1 key1, K2 key2, V value) {
        map1.put(key1, value);
        map2.put(key2, value);
    }
}
Run Code Online (Sandbox Code Playgroud)