简体   繁体   English

如何创建具有两个键(键对、值)的 HashMap?

[英]How to create a HashMap with two keys (Key-Pair, Value)?

I have a 2D array of Integers.我有一个二维整数数组。 I want them to be put into a HashMap.我希望将它们放入 HashMap 中。 But I want to access the elements from the HashMap based on Array Index.但我想根据数组索引访问 HashMap 中的元素。 Something like:就像是:

For A[2][5], map.get(2,5) which returns a value associated with that key.对于 A[2][5], map.get(2,5)返回与该键关联的值。 But how do I create a hashMap with a pair of keys?但是如何使用一对键创建一个 hashMap 呢? Or in general, multiple keys: Map<((key1, key2,..,keyN), Value) in a way that I can access the element with using get(key1,key2,...keyN).或者一般来说,多个键: Map<((key1, key2,..,keyN), Value) ,我可以使用 get(key1,key2,...keyN) 访问元素。

EDIT : 3 years after posting the question, I want to add a bit more to it编辑:发布问题 3 年后,我想添加更多内容

I came across another way for NxN matrix .我遇到了NxN matrix的另一种方式。

Array indices, i and j can be represented as a single key the following way:数组索引ij可以通过以下方式表示为单个key

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

And the indices can be retrevied from the key in this way:并且可以通过这种方式从key中检索索引:

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

There are several options:有几种选择:

2 dimensions 2 维

Map of maps地图地图

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

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

Wrapper key object包装键对象

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

}

Implementing equals() and hashCode() is crucial here.实现equals()hashCode()在这里至关重要。 Then you simply use:然后你只需使用:

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

and:和:

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

Table from Guava番石榴的Table

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

table.get(2, 5);

Table uses map of maps underneath. Table使用下面的地图

N dimensions N 维

Notice that special Key class is the only approach that scales to n-dimensions.请注意,特殊的Key类是唯一可以扩展到 n 维的方法。 You might also consider:您还可以考虑:

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

but that's terrible from performance perspective, as well as readability and correctness (no easy way to enforce list size).但从性能的角度来看,这很糟糕,可读性和正确性也很糟糕(没有简单的方法来强制执行列表大小)。

Maybe take a look at Scala where you have tuples and case classes (replacing whole Key class with one-liner).也许看看你有元组和case类的 Scala(用单线替换整个Key类)。

When you create your own key pair object, you should face a few thing.当您创建自己的密钥对对象时,您应该面对一些事情。

First, you should be aware of implementing hashCode() and equals() .首先,您应该知道实现hashCode()equals() You will need to do this.您将需要这样做。

Second, when implementing hashCode() , make sure you understand how it works.其次,在实现hashCode()时,请确保您了解它的工作原理。 The given user example给定的用户示例

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

is actually one of the worst implementations you can do.实际上是您可以做的最糟糕的实现之一。 The reason is simple: you have a lot of equal hashes!原因很简单:你有很多相等的哈希值! And the hashCode() should return int values that tend to be rare, unique at it's best.并且hashCode()应该返回往往很少见的 int 值,最好是唯一的。 Use something like this:使用这样的东西:

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

This is fast and returns unique hashes for keys between -2^16 and 2^16-1 (-65536 to 65535).这速度很快,并为 -2^16 和 2^16-1(-65536 到 65535)之间的键返回唯一的哈希值。 This fits in almost any case.这几乎适用于任何情况。 Very rarely you are out of this bounds.很少你会超出这个界限。

Third, when implementing equals() also know what it is used for and be aware of how you create your keys, since they are objects.第三,在实现equals()时,还要知道它的用途并了解如何创建密钥,因为它们是对象。 Often you do unnecessary if statements cause you will always have the same result.通常你会做不必要的 if 语句,因为你总是会得到相同的结果。

If you create keys like this: map.put(new Key(x,y),V);如果您创建这样的键: map.put(new Key(x,y),V); you will never compare the references of your keys.你永远不会比较你的钥匙的参考。 Cause everytime you want to acces the map, you will do something like map.get(new Key(x,y));因为每次你想访问地图时,你都会做类似map.get(new Key(x,y)); . . Therefore your equals() does not need a statement like if (this == obj) .因此,您的equals()不需要像if (this == obj)的语句。 It will never occure.永远不会发生。

Instead of if (getClass() != obj.getClass()) in your equals() better use if (!(obj instanceof this)) .而不是if (getClass() != obj.getClass())在你的equals()更好地使用if (!(obj instanceof this)) It will be valid even for subclasses.它甚至对子类也有效。

So the only thing you need to compare is actually X and Y. So the best equals() implementation in this case would be:所以你唯一需要比较的是 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;
}

So in the end your key class is like this:所以最后你的关键类是这样的:

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

}

You can give your dimension indices X and Y a public access level, due to the fact they are final and do not contain sensitive information.您可以为维度索引XY提供公共访问级别,因为它们是最终的并且不包含敏感信息。 I'm not a 100% sure whether private access level works correctly in any case when casting the Object to a Key .在将Object转换为Key时,我不能 100% 确定private访问级别在任何情况下是否都能正常工作。

If you wonder about the finals, I declare anything as final which value is set on instancing and never changes - and therefore is an object constant.如果你想知道决赛,我将任何东西声明为 final,它的值是在实例化时设置的并且永远不会改变 - 因此是一个对象常量。

You can't have an hash map with multiple keys, but you can have an object that takes multiple parameters as the key.您不能拥有一个包含多个键的哈希映射,但您可以拥有一个以多个参数作为键的对象。

Create an object called Index that takes an x and y value.创建一个名为 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;
    }
}

Then have your HashMap<Index, Value> to get your result.然后让你的HashMap<Index, Value>得到你的结果。 :) :)

在通用集合MultiKeyMap中实现

Two possibilities.两种可能。 Either use a combined key:要么使用组合键:

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

Or a Map of Map:或地图地图:

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

Use a Pair as keys for the HashMap .使用Pair作为HashMap的键。 JDK has no Pair, but you can either use a 3rd party libraray such as http://commons.apache.org/lang or write a Pair taype of your own. JDK 没有 Pair,但您可以使用 3rd 方库,例如http://commons.apache.org/lang或编写自己的 Pair taype。

Java 7+ contains a new Map.Entry<K,V> class that you can use as key for your map (or entry for your set). Java 7+ 包含一个新的Map.Entry<K,V>类,您可以将其用作地图的键(或集合的条目)。 Java 9+ also contains a Map.entry(K k, V v) method to easily create new Map.Entry objects. Java 9+ 还包含一个Map.entry(K k, V v)方法来轻松创建新的Map.Entry对象。

Usage:用法:

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

There is alsoPair<K, V> in javafx.util javafx.util 中还有Pair<K, V>

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

Create a value class that will represent your compound key, such as:创建一个代表复合键的值类,例如:

class Index2D {
  int first, second;

  // overrides equals and hashCode properly here
}

taking care to override equals() and hashCode() correctly.注意正确覆盖equals()hashCode() If that seems like a lot of work, you might consider some ready made generic containers, such as Pair provided by apache commons among others.如果这看起来工作量很大,您可能会考虑一些现成的通用容器,例如 apache commons 提供的Pair等。

There are also many similar questions here, with other ideas, such as using Guava's Table , although allows the keys to have different types, which might be overkill (in memory use and complexity) in your case since I understand your keys are both integers.这里也有许多类似的问题,还有其他想法,例如使用 Guava's Table ,尽管允许键具有不同的类型,在您的情况下这可能是过度的(在内存使用和复杂性方面),因为我知道您的键都是整数。

If they are two integers you can try a quick and dirty trick: Map<String, ?> using the key as i+"#"+j .如果它们是两个整数,您可以尝试一个快速而肮脏的技巧: Map<String, ?>使用键作为i+"#"+j

If the key i+"#"+j is the same as j+"#"+i try min(i,j)+"#"+max(i,j) .如果键i+"#"+jj+"#"+i相同,请尝试min(i,j)+"#"+max(i,j)

You could create your key object something like this:你可以像这样创建你的关键对象:

public class MapKey {公共类 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;
}

} }

The advantage of this is: It will always make sure you are covering all the scenario's of Equals as well.这样做的好处是:它将始终确保您也涵盖了 Equals 的所有场景。

NOTE : Your key1 and key2 should be immutable.注意:您的 key1 和 key2 应该是不可变的。 Only then will you be able to construct a stable key Object.只有这样你才能构造一个稳定的关键对象。

You can also use guava Table implementation for this.您也可以为此使用guava Table实现。

Table represents a special map where two keys can be specified in combined fashion to refer to a single value.表表示一个特殊的映射,其中可以以组合方式指定两个键来引用单个值。 It is similar to creating a map of maps.它类似于创建地图的地图。

//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");

we can create a class to pass more than one key or value and the object of this class can be used as a parameter in 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();

        }

    }

    }

 }

You can download it from the below link: https://github.com/VVS279/DoubleKeyHashMap/blob/master/src/com/virtualMark/doubleKeyHashMap/DoubleKeyHashMap.java您可以从以下链接下载它: https ://github.com/VVS279/DoubleKeyHashMap/blob/master/src/com/virtualMark/doubleKeyHashMap/DoubleKeyHashMap.java

https://github.com/VVS279/DoubleKeyHashMap https://github.com/VVS279/DoubleKeyHashMap

You can use double key: value hashmap,您可以使用双键:值哈希图,

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

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

Using org.apache.commons.lang3.tuple.Pair is very easy;使用 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();
   

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM