如何创建一个有两个键(键对,值)的 HashMap?

我有一个2D 整数数组。我想把它们放到一个 HashMap 中。但是我想基于 Array Index 访问 HashMap 中的元素。比如:

对于 A [2][5] ,map.get(2,5)返回与该键关联的值。但是如何使用一对键创建 hashMap 呢?或者一般来说,多个键: Map<((key1, key2,..,keyN), Value),我可以使用 get (key1,key2,... keyN)访问元素。

编辑: 3年后发布的问题,我想添加一点更多的

我找到了 NxN matrix的另一条路。

数组索引、 ij可以用以下方式表示为单个 key:

int key = i * N + j;
//map.put(key, a[i][j]); // queue.add(key);

这些指数可以通过这种方式从 key中重新检索:

int i = key / N;
int j = key % N;
214454 次浏览

有两种可能,要么使用组合键:

class MyKey {
int firstIndex;
int secondIndex;
// important: override hashCode() and equals()
}

或者一张地图:

Map<Integer, Map<Integer, Integer>> myMap;

创建一个表示复合键的值类,例如:

class Index2D {
int first, second;


// overrides equals and hashCode properly here
}

注意正确覆盖 equals()hashCode()。如果这看起来工作量很大,那么您可以考虑一些现成的通用容器,比如由 apachecommons 等提供的 Pair

这里也有很多 类似的问题,还有一些其他的想法,比如使用 Guava 的 桌子,但是允许键有不同的类型,这在您的情况下可能有些过度(在内存使用和复杂性方面) ,因为我知道您的键都是整数。

使用 Pair作为 HashMap的键。JDK 没有 Pair,但是您可以使用第三方库(如 http://commons.apache.org/lang)或编写自己的 Pair 类型。

有几种选择:

2维

地图

Map<Integer, Map<Integer, V>> map = //...
//...


map.get(2).get(5);

包装器密钥对象

public class Key {


private final int x;
private final int y;


public Key(int x, int y) {
this.x = x;
this.y = y;
}


@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Key)) return false;
Key key = (Key) o;
return x == key.x && y == key.y;
}


@Override
public int hashCode() {
int result = x;
result = 31 * result + y;
return result;
}


}

实现 equals()hashCode()在这里是至关重要的:

Map<Key, V> map = //...

以及:

map.get(new Key(2, 5));

来自番石榴的 Table

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


table.get(2, 5);

Table在下面使用 地图地图

N 维

请注意,特殊的 Key类是唯一可伸缩到 n 维的方法。您可能还会考虑:

Map<List<Integer>, V> map = //...

但是从性能角度来看,这很糟糕,而且可读性和正确性也很差(强制执行列表大小并不容易)。

也许可以看看 Scala,其中有元组和 case类(用一行程序代替整个 Key类)。

您不能拥有具有多个键的散列映射,但是您可以拥有以多个参数作为键的对象。

创建一个名为 Index 的对象,该对象接受 x 和 y 值。

public class Index {


private int x;
private int y;


public Index(int x, int y) {
this.x = x;
this.y = y;
}


@Override
public int hashCode() {
return this.x ^ this.y;
}


@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Index other = (Index) obj;
if (x != other.x)
return false;
if (y != other.y)
return false;
return true;
}
}

然后让你的 HashMap<Index, Value>得到你的结果。 :)

如果它们是两个整数,您可以尝试一个快速和肮脏的技巧: Map<String, ?>使用键作为 i+"#"+j

如果键 i+"#"+jj+"#"+i相同,请尝试 min(i,j)+"#"+max(i,j)

当您创建自己的密钥对对象时,您应该面对一些问题。

首先,您应该注意实现 hashCode()equals()

其次,在实现 hashCode()时,确保您了解它是如何工作的

public int hashCode() {
return this.x ^ this.y;
}

实际上是你能做的最糟糕的实现之一。原因很简单: 您有许多相等的散列!而且 hashCode()应该返回的 int 值往往是罕见的,最好是唯一的。使用这样的东西:

public int hashCode() {
return (X << 16) + Y;
}

这很快,并且返回 -2 ^ 16和2 ^ 16-1(- 65536到65535)之间的键的唯一散列。这几乎适用于任何情况。你很少会越界。

第三,在实现 equals()时,还要知道它的用途,并且要知道如何创建键,因为它们是对象。你经常做不必要的如果陈述导致你总是有相同的结果。

如果您像这样创建键: map.put(new Key(x,y),V);,您将永远不会比较您的键的引用。因为每次你想访问地图,你会做一些像 map.get(new Key(x,y));。因此,您的 equals()不需要像 if (this == obj)这样的语句。它将 永远不会发生。

在你的 equals()中最好使用 if (!(obj instanceof this))代替 if (getClass() != obj.getClass())。它甚至对子类也是有效的。

所以你唯一需要比较的是 X 和 Y。所以在这种情况下,最好的 equals()实现应该是:

public boolean equals (final Object O) {
if (!(O instanceof Key)) return false;
if (((Key) O).X != X) return false;
if (((Key) O).Y != Y) return false;
return true;
}

所以最后你的关键类是这样的:

public class Key {


public final int X;
public final int Y;


public Key(final int X, final int Y) {
this.X = X;
this.Y = Y;
}


public boolean equals (final Object O) {
if (!(O instanceof Key)) return false;
if (((Key) O).X != X) return false;
if (((Key) O).Y != Y) return false;
return true;
}


public int hashCode() {
return (X << 16) + Y;
}
    

}

您可以给维度索引 XY一个公共访问级别,因为它们是最终的,不包含敏感信息。我不能100% 肯定 private访问级别是否正确工作在 任何的情况下,当铸造的 Object到一个 Key

如果你想知道 final,我将任何东西声明为 final,它的值设置在实例上并且永远不变——因此它是一个对象常量。

在公共集合中实现

你可以像这样创建你的关键对象:

公共类 MapKey {

public  Object key1;
public Object key2;


public Object getKey1() {
return key1;
}


public void setKey1(Object key1) {
this.key1 = key1;
}


public Object getKey2() {
return key2;
}


public void setKey2(Object key2) {
this.key2 = key2;
}


public boolean equals(Object keyObject){


if(keyObject==null)
return false;


if (keyObject.getClass()!= MapKey.class)
return false;


MapKey key = (MapKey)keyObject;


if(key.key1!=null && this.key1==null)
return false;


if(key.key2 !=null && this.key2==null)
return false;


if(this.key1==null && key.key1 !=null)
return false;


if(this.key2==null && key.key2 !=null)
return false;


if(this.key1==null && key.key1==null && this.key2 !=null && key.key2 !=null)
return this.key2.equals(key.key2);


if(this.key2==null && key.key2==null && this.key1 !=null && key.key1 !=null)
return this.key1.equals(key.key1);


return (this.key1.equals(key.key1) && this.key2.equals(key2));
}


public int hashCode(){
int key1HashCode=key1.hashCode();
int key2HashCode=key2.hashCode();
return key1HashCode >> 3 + key2HashCode << 5;
}

}

这样做的好处是: 它将始终确保您涵盖了 Equals 的所有场景。

注意 : 您的 key1和 key2应该是不可变的。只有这样,您才能够构造一个稳定的 key Object。

也可以使用 番石榴桌实现。

表表示一个特殊的映射,其中可以以组合方式指定两个键来引用单个值。它类似于创建地图的地图。

//create a table
Table<String, String, String> employeeTable = HashBasedTable.create();


//initialize the table with employee details
employeeTable.put("IBM", "101","Mahesh");
employeeTable.put("IBM", "102","Ramesh");
employeeTable.put("IBM", "103","Suresh");


employeeTable.put("Microsoft", "111","Sohan");
employeeTable.put("Microsoft", "112","Mohan");
employeeTable.put("Microsoft", "113","Rohan");


employeeTable.put("TCS", "121","Ram");
employeeTable.put("TCS", "122","Shyam");
employeeTable.put("TCS", "123","Sunil");


//get Map corresponding to IBM
Map<String,String> ibmEmployees =  employeeTable.row("IBM");

我们可以创建一个类来传递多个键或值,这个类的对象可以用作 map 中的一个参数。

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.util.*;


public class key1 {
String b;
String a;
key1(String a,String b)
{
this.a=a;
this.b=b;
}
}


public class read2 {


private static final String FILENAME = "E:/studies/JAVA/ReadFile_Project/nn.txt";


public static void main(String[] args) {


BufferedReader br = null;
FileReader fr = null;
Map<key1,String> map=new HashMap<key1,String>();
try {


fr = new FileReader(FILENAME);
br = new BufferedReader(fr);


String sCurrentLine;


br = new BufferedReader(new FileReader(FILENAME));


while ((sCurrentLine = br.readLine()) != null) {
String[] s1 = sCurrentLine.split(",");
key1 k1 = new key1(s1[0],s1[2]);
map.put(k1,s1[2]);
}
for(Map.Entry<key1,String> m:map.entrySet()){
key1 key = m.getKey();
String s3 = m.getValue();
System.out.println(key.a+","+key.b+" : "+s3);
}
//            }
} catch (IOException e) {


e.printStackTrace();


} finally {


try {


if (br != null)
br.close();


if (fr != null)
fr.close();


} catch (IOException ex) {


ex.printStackTrace();


}


}


}


}

你可在以下连结下载: Https://github.com/vvs279/doublekeyhashmap/blob/master/src/com/virtualmark/doublekeyhashmap/doublekeyhashmap.java

Https://github.com/vvs279/doublekeyhashmap

您可以使用双键: value 散列表,

   DoubleKeyHashMap<Integer, Integer, String> doubleKeyHashMap1 = new
DoubleKeyHashMap<Integer, Integer, String>();


DoubleKeyHashMap<String, String, String> doubleKeyHashMap2 = new
DoubleKeyHashMap<String, String, String>();

Java7 + 包含一个新的 Map.Entry<K,V>类,您可以将它用作映射的键(或集合的条目)。Java9 + 还包含一个 Map.entry(K k, V v)方法,可以轻松地创建新的 Map.Entry对象。

用法:

Map<Map.Entry<Integer,Integer>, Integer> map = new HashMap<>();
map.put(Map.entry(1, 2), 0);

在 javafx.util 中也有 Pair<K, V>

Map<Pair<Integer,Integer>, Integer> map = new HashMap<>();
map.put(new Pair(1, 2), 0);

使用 org.apache.commons. lang3.tuple

   Map<String, Pair<String, Integer>> map= new HashMap<>();
map.put("key", Pair.of("a", 1));
int value = map.get("key").getRight();