132

我有一个二维整数数组。我希望将它们放入 HashMap 中。但我想根据数组索引访问 HashMap 中的元素。就像是:

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

编辑:发布问题 3 年后,我想添加更多内容

我遇到了另一种方式NxN matrix

数组索引,i并且可以通过以下方式j表示为单个:key

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

并且可以通过key以下方式检索索引:

int i = key / N;
int j = key % N;
4

14 回答 14

208

有几种选择:

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

于 2013-02-03T22:25:35.787 回答
26

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

首先,您应该了解实现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()还要知道它的用途并注意如何创建密钥,因为它们是对象。通常你会做不必要的 if 语句,因为你总是会得到相同的结果。

如果您像这样创建密钥:map.put(new Key(x,y),V);您将永远不会比较密钥的引用。因为每次您想要访问地图时,您都会执行类似map.get(new Key(x,y));. 因此,您equals()不需要像if (this == obj). 它永远不会发生。

而不是if (getClass() != obj.getClass())在您equals()更好地使用if (!(obj instanceof this)). 它甚至对子类也有效。

所以你唯一需要比较的是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 %确定privateObject.Key

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

于 2014-11-17T21:02:29.990 回答
7

您不能拥有一个包含多个键的哈希映射,但您可以拥有一个以多个参数作为键的对象。

创建一个名为 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>得到你的结果。:)

于 2013-02-03T22:25:36.083 回答
7

在通用集合MultiKeyMap中实现

于 2014-11-25T08:33:41.917 回答
4

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

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

或地图地图:

Map<Integer, Map<Integer, Integer>> myMap;
于 2013-02-03T22:24:10.717 回答
4

使用 aPair作为 的键HashMap。JDK 没有 Pair,但您可以使用 3rd 方库,例如http://commons.apache.org/lang或编写自己的 Pair taype。

于 2013-02-03T22:25:10.153 回答
2

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

用法:

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

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

Map<Pair<Integer,Integer>, Integer> map = new HashMap<>();
map.put(new Pair(1, 2), 0);
于 2021-08-02T03:50:20.173 回答
1

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

公共类 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 应该是不可变的。只有这样你才能构造一个稳定的关键对象。

于 2015-02-24T19:58:53.420 回答
1

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

class Index2D {
  int first, second;

  // overrides equals and hashCode properly here
}

注意覆盖equals()hashCode()正确。如果这看起来工作量很大,您可能会考虑一些现成的通用容器,例如Pairapache commons 等提供的。

这里也有许多类似的问题,还有其他想法,例如使用 Guava's Table,尽管允许键具有不同的类型,在您的情况下这可能是过度的(在内存使用和复杂性方面),因为我知道您的键都是整数。

于 2013-02-03T22:24:40.103 回答
1

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

如果 key与tryi+"#"+j相同。j+"#"+imin(i,j)+"#"+max(i,j)

于 2013-02-03T22:29:54.210 回答
1

我们可以创建一个类来传递多个键或值,并且该类的对象可以用作映射中的参数。

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

        }

    }

    }

 }
于 2017-05-25T13:33:49.987 回答
1

您也可以为此使用guava Table实现。

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

//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");
于 2016-12-14T12:56:04.033 回答
0

您可以从以下链接下载它: https ://github.com/VVS279/DoubleKeyHashMap/blob/master/src/com/virtualMark/doubleKeyHashMap/DoubleKeyHashMap.java

https://github.com/VVS279/DoubleKeyHashMap

您可以使用双键:值哈希图,

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

   DoubleKeyHashMap<String, String, String> doubleKeyHashMap2 = new 
   DoubleKeyHashMap<String, String, String>();
于 2019-08-20T09:30:51.647 回答
0

使用 org.apache.commons.lang3.tuple.Pair 非常简单;

   Map<String, Pair<String, Integer>> map= new HashMap<>(); 
   map.put("key", Pair.of("a", 1)); 
   int value = map.get("key").getRight();
   
于 2021-12-21T19:59:49.540 回答