java - metodos - ¿Cómo crear un HashMap con dos claves(par de claves, valor)?
metodos de hashmap java (11)
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 genéricos 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.
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;
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 declaración 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.
Dos posibilidades O use una clave combinada:
class MyKey {
int firstIndex;
int secondIndex;
// important: override hashCode() and equals()
}
O un mapa de mapa:
Map<Integer, Map<Integer, Integer>> myMap;
Hay varias opciones:
2 dimensiones
Mapa de mapas
Map<Integer, Map<Integer, V>> 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<Key, V> map = //...
y:
map.get(new Key(2, 5));
Table
de guayaba
Table<Integer, Integer, V> 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<List<Integer>, 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).
Implementado en colecciones comunes [MultiKeyMap] ( https://commons.apache.org/proper/commons-collections/javadocs/api-release/org/apache/commons/collections4/map/MultiKeyMap.html )
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<Index, Value>
para obtener su resultado. :)
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.
Si son dos enteros, puede intentar un truco rápido y sucio: ¿ Map<String, ?>
Usando la tecla como i+"#"+j
.
Si la clave i+"#"+j
es lo mismo que j+"#"+i
intento min(i,j)+"#"+max(i,j)
.
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<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");
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.
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<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();
}
}
}
}