Java 如何使用两个键(键对、值)创建HashMap?

Java 如何使用两个键(键对、值)创建HashMap?,java,hash,hashmap,hashcode,Java,Hash,Hashmap,Hashcode,我有一个二维整数数组。我想把它们放到一个HashMap中。但是我想基于数组索引访问HashMap中的元素。比如: 对于[2][5],map.get2,5返回与该键关联的值。但是我如何用一对键创建hashMap呢?或者通常,多个键:Map两种可能性。或者使用组合键: class MyKey { int firstIndex; int secondIndex; // important: override hashCode() and equals() } 或一张地图: M

我有一个二维整数数组。我想把它们放到一个HashMap中。但是我想基于数组索引访问HashMap中的元素。比如:


对于[2][5],map.get2,5返回与该键关联的值。但是我如何用一对键创建hashMap呢?或者通常,多个键:Map两种可能性。或者使用组合键:

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。如果这看起来像很多工作,您可以考虑一些现成的通用容器,例如Apache CuMon提供的对。
这里也有很多,还有其他的想法,比如使用番石榴,尽管允许键有不同的类型,这可能会过度消耗内存,并且在您的情况下会变得复杂,因为我知道您的键都是整数

使用一对作为HashMap的键。JDK没有Pair,但您可以使用第三方Librarray,如或编写自己的Pair类型。

有几个选项:

二维 地图 在这里,实现equals和hashCode是至关重要的。然后,您只需使用:

Map<Key, V> map = //...
番石榴 但从性能的角度来看,这很糟糕,可读性和正确性也很糟糕——这并不是强制执行列表大小的简单方法


也许可以看看Scala,在Scala中,元组和case类用一个线性行替换整个Key类。

不能有一个包含多个Key的哈希映射,但可以有一个将多个参数作为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获得结果:

如果它们是两个整数,您可以尝试一个快速而肮脏的技巧:使用键i++j进行映射


如果密钥i++j与j++相同,我会尝试mini,j++maxi,j.

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

首先,您应该知道如何实现hashCode和equals。你需要这样做

其次,在实现hashCode时,请确保您了解它是如何工作的。给定的用户示例

public int hashCode() {
    return this.x ^ this.y;
}
实际上是你能做的最糟糕的实现之一。原因很简单:你有很多相同的哈希!hashCode应该返回int值,这些值通常很少,最好是唯一的。使用类似以下内容:

public int hashCode() {
  return (X << 16) + Y;
}
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;
  }

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

public int hashCode() {
  return (X << 16) + Y;
}
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;
  }

}
您可以为维度索引X和Y提供公共访问级别,因为它们是最终的,不包含敏感信息。在将对象强制转换为密钥时,我不能100%确定私有访问级别在任何情况下是否正常工作


如果您想知道最终版本,我声明任何值在实例化时设置且从不更改的内容都是最终版本,因此它是一个对象常量。

在常见集合中实现。

您可以创建如下关键对象:

public int hashCode() {
  return (X << 16) + Y;
}
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;
  }

}
公共类映射键{

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

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

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

您也可以使用guava表实现

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

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

        }

    }

    }

 }

您可以从以下链接下载:

可以使用双键:值hashmap

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

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

一个简单的解决方案是将一个键映射到另一个hashmap。请不要回答问题中的问题。您的编辑很有趣,所以请随意将其作为答案发布。@Crocode哇!编辑中答案背后的数学知识非常丰富。只是想知道它是否适用于任意两个整数i和j。@Crocode如果它们是N的倍数,i和j会循环吗?如果您不关心性能或内存使用,只使用映射映射,即映射很小,或者有许多键具有相同的第一个索引-因为此解决方案意味着为每个唯一的第一个索引支付HashMap对象的开销。要改进此答案,请提供有关重写hashCode和equals方法的信息。您需要重写hashCode和equals。hashCode实现没有区分2,1和1,2这两种方法碰撞。Hashcode不需要为每个不同的对象保证不同的值@用户1947415真是个坏主意。首先,这很糟糕。其次,这种技术将被复制到其他类型,不同的键可能会映射到同一个字符串上,结果很滑稽。在我看来,当i==j时,ij=ji,所以使用最小/最大技巧是不行的。@Matthieu交换55和55之间有什么区别?@enrey None。这就是我要指出的。
这真的取决于你对钥匙的了解。@Matthieu-aha我明白你的意思。我认为@arutaku的意思是,当你想要53像35一样有相同的散列,然后你用min/max按这个顺序强制35。嗨,其他人在做散列代码时有x'或两个值。你为什么用31?我认为它与32位整数有关,但当我考虑它时,它没有意义,因为x=1和y=0仍然映射到与x=0和y相同的哈希代码=31@pete约书亚·布洛赫(Joshua Bloch),有效Java第3章。第9条建议,1。在一个名为result…(结果)的int变量中存储一些常量非零值,比如17,如果它是素数,则在冲突方面效果更好。另请参见:为什么不使用Map.Entry作为键来代替包装器键对象?Map呢?请注意,hashCode也可以用一行作为Objects.hashx来实现,y@Wilson我现在修复了链接,等待同行review@computingfreak似乎有一个有利的观点。万岁!注意:这是最好的答案。除非你想花上几个小时和Apache专家工程师竞争一些非常有用但最终很平凡的功能。
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;
}
//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");
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();

        }

    }

    }

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

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