¿Cómo crear un HashMap con dos claves (par de claves, valor)?

Tengo una matriz 2D de enteros. Quiero que se pongan en un HashMap. Pero quiero acceder a los elementos de HashMap según el Índice de matriz. Algo como:

Para A [2] [5], map.get(2,5) que devuelve un valor asociado con esa clave. Pero, ¿cómo creo un hashMap con un par de claves? O, en general, varias teclas: Map<((key1, key2,..,keyN), Value) de forma que pueda acceder al elemento con el uso de get (clave1, clave2, … teclaN).

EDITAR: 3 años después de publicar la pregunta, quiero agregarle un poco más

Encontré otra forma para la NxN matrix .

Los índices de matriz, i y j se pueden representar como una sola key la siguiente manera:

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

Y los índices se pueden retrevied de la key de esta manera:

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

Hay varias opciones:

2 dimensiones

Mapa de mapas

 Map> map = //... //... map.get(2).get(5); 

Objeto clave de contenedor

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

La implementación de equals() y hashCode() es crucial aquí. Entonces simplemente usa:

 Map map = //... 

y:

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

Table de guayaba

 Table table = HashBasedTable.create(); //... table.get(2, 5); 

Table usa un mapa de mapas debajo.

N dimensiones

Observe que la clase Key especial es el único enfoque que se escala a n dimensiones. También podrías considerar:

 Map, V> map = //... 

pero eso es terrible desde la perspectiva del rendimiento, así como de la legibilidad y corrección (no es una forma fácil de hacer cumplir el tamaño de la lista).

Tal vez eche un vistazo a Scala donde tiene tuplas y clases de case (reemplazando toda la clase Key con un solo liner).

Cuando crea su propio objeto de par de claves, debe enfrentar algunas cosas.

En primer lugar, debe tener en cuenta la implementación de hashCode() y equals() . Tendrás que hacer esto.

En segundo lugar, al implementar hashCode() , asegúrese de comprender cómo funciona. El ejemplo de usuario dado

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

es en realidad una de las peores implementaciones que puedes hacer. La razón es simple: ¡tienes muchos hash iguales! Y el hashCode() debería devolver valores int que tienden a ser raros, únicos en el mejor de los casos. Usa algo como esto:

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

Esto es rápido y devuelve hashes únicos para claves entre -2 ^ 16 y 2 ^ 16-1 (-65536 a 65535). Esto se adapta en casi cualquier caso. Muy rara vez estás fuera de estos límites.

En tercer lugar, cuando implemente equals() también sepa para qué se utiliza y tenga en cuenta cómo crea las claves, ya que son objetos. A menudo no es necesario si las declaraciones causan que siempre tendrá el mismo resultado.

Si crea claves como esta: map.put(new Key(x,y),V); nunca compararás las referencias de tus llaves. Porque cada vez que quieras acceder al mapa, harás algo como map.get(new Key(x,y)); . Por lo tanto, tus equals() no necesitan una statement como if (this == obj) . Nunca ocurrirá.

En lugar de if (getClass() != obj.getClass()) en su equals() use mejor if (!(obj instanceof this)) . Será válido incluso para subclases.

Entonces, lo único que necesita comparar es en realidad X e Y. Entonces, la mejor implementación de equals() en este caso sería:

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

Así que al final tu clase de clave es así:

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

Puede dar a sus índices de dimensión X e Y un nivel de acceso público, ya que son finales y no contienen información confidencial. No estoy 100% seguro de si private nivel de acceso private funciona correctamente en cualquier caso al convertir el Object en una Key .

Si te preguntas acerca de las finales, declaro cualquier cosa como final, cuyo valor se establece en la creación de instancias y nunca cambia, y por lo tanto es un objeto constante.

No puede tener un mapa hash con múltiples claves, pero puede tener un objeto que tome múltiples parámetros como la clave.

Cree un objeto llamado Índice que tome un valor xey.

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

Luego, tenga su HashMap para obtener su resultado. 🙂

Dos posibilidades O use una clave combinada:

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

O un mapa de mapa:

 Map> myMap; 

Cree una clase de valor que represente su clave compuesta, como por ejemplo:

 class Index2D { int first, second; // overrides equals and hashCode properly here } 

teniendo cuidado de anular equals() y hashCode() correctamente. Si parece mucho trabajo, puede considerar algunos contenedores generics listos, como Pair proporcionado por apache commons, entre otros.

También hay muchas preguntas similares aquí, con otras ideas, como usar Guava’s Table , aunque permite que las claves tengan diferentes tipos, lo que puede ser exagerado (en uso de memoria y complejidad) en su caso ya que entiendo que sus claves son ambos enteros.

Si son dos enteros, puede intentar un truco rápido y sucio: ¿ Map Usando la tecla como i+"#"+j .

Si la clave i+"#"+j es lo mismo que j+"#"+i bash min(i,j)+"#"+max(i,j) .

Podrías crear tu objeto clave algo como esto:

clase pública 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; } 

}

La ventaja de esto es: siempre se asegurará de que estés cubriendo todos los escenarios de Iguales también.

NOTA : Su clave1 y clave2 deben ser inmutables. Solo entonces podrás construir un objeto clave estable.

podemos crear una clase para pasar más de una clave o valor y el objeto de esta clase se puede usar como un parámetro en el mapa.

 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 map=new HashMap(); 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 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(); } } } } 

Use un Pair como claves para HashMap . JDK no tiene Pair, pero puedes usar un libraray de terceros como http://commons.apache.org/lang o escribir un par de canciones tuyas.

También puede usar la implementación de la tabla de guayaba para esto.

La tabla representa un mapa especial donde dos teclas se pueden especificar de manera combinada para referirse a un único valor. Es similar a crear un mapa de mapas.

 //create a table Table 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 ibmEmployees = employeeTable.row("IBM");