ArrayList como clave en HashMap

¿Sería posible agregar una ArrayList como la clave de HashMap ? Me gustaría mantener el recuento de frecuencia de bigrams. El bigram es la clave y el valor es su frecuencia.

Para cada uno de los bigrams como “él es”, creo un ArrayList para él y lo inserto en el HashMap . Pero no estoy obteniendo el resultado correcto.

 public HashMap<ArrayList, Integer> getBigramMap(String word1, String word2) { HashMap<ArrayList, Integer> hm = new HashMap<ArrayList, Integer>(); ArrayList arrList1 = new ArrayList(); arrList1 = getBigram(word1, word2); if (hm.get(arrList1) != null) { hm.put(arrList1, hm.get(arrList1) + 1); } else { hm.put(arrList1, 1); } System.out.println(hm.get(arrList1)); return hm; } public ArrayList getBigram(String word1, String word2) { ArrayList arrList2 = new ArrayList(); arrList2.add(word1); arrList2.add(word2); return arrList2; } 

Sí, puede tener ArrayList como claves en un mapa hash, pero es una muy mala idea ya que son mutables .

Si modifica ArrayList de alguna manera (o cualquiera de sus elementos), la asignación se perderá básicamente, ya que la clave no tendrá el mismo hashCode que cuando se insertó.

La regla práctica es usar solo tipos de datos inmutables como claves en un mapa hash. Como sugirió Alex Stybaev, es probable que desee crear una clase de Bigram como esta:

 final class Bigram { private final String word1, word2; public Bigram(String word1, String word2) { this.word1 = word1; this.word2 = word2; } public String getWord1() { return word1; } public String getWord2() { return word2; } @Override public int hashCode() { return word1.hashCode() ^ word2.hashCode(); } @Override public boolean equals(Object obj) { return (obj instanceof Bigram) && ((Bigram) obj).word1.equals(word1) && ((Bigram) obj).word2.equals(word2); } } 

De la documentación :

Nota: se debe tener mucho cuidado si los objetos mutables se utilizan como claves de mapa. El comportamiento de un mapa no se especifica si el valor de un objeto se cambia de una manera que afecta equals comparaciones equals mientras el objeto es una clave en el mapa. Un caso especial de esta prohibición es que no está permitido que un mapa se contenga como una clave. Si bien es permisible que un mapa se contenga a sí mismo como un valor, se recomienda extrema precaución: los métodos hashCode y hashCode ya no están bien definidos en dicho mapa.

hashCode tener cuidado cuando usa objetos mutables como claves por el hashCode y equals .

La conclusión es que es mejor usar objetos inmutables como llaves.

¿Por qué no puedes usar algo como esto?

 class Bigram{ private String firstItem; private String secondItem;  @Override public int hashCode(){ ... } @Override public boolean equals(){ ... } } 

en lugar de usar la colección dinámica para un número limitado de elementos (dos).

Prueba esto, esto funcionará.

  public Map getBigramMap (String word1,String word2){ Map hm = new HashMap(); List arrList1 = new ArrayList(); arrList1 = getBigram(word1, word2); if(hm.get(arrList1) !=null){ hm.put(arrList1, hm.get(arrList1)+1); } else { hm.put(arrList1, 1); } System.out.println(hm.get(arrList1)); return hm; } 

He encontrado esta solución. Obviamente, no se puede usar en todos los casos, por ejemplo, sobrepasar la capacidad int de hashcodes o complicaciones de list.clone () (si se cambia la lista de entrada, la clave permanece igual a la intencionada, pero cuando los elementos de List son mutables, clonados lista tiene la misma referencia a sus elementos, lo que daría como resultado el cambio de la clave en sí).

 import java.util.ArrayList; public class ListKey { private ArrayList list; public ListKey(ArrayList list) { this.list = (ArrayList) list.clone(); } @Override public int hashCode() { final int prime = 31; int result = 1; for (int i = 0; i < this.list.size(); i++) { T item = this.list.get(i); result = prime * result + ((item == null) ? 0 : item.hashCode()); } return result; } @Override public boolean equals(Object obj) { return this.list.equals(obj); } } --------- public static void main(String[] args) { ArrayList createFloatList = createFloatList(); ArrayList createFloatList2 = createFloatList(); Hashtable, String> table = new Hashtable<>(); table.put(new ListKey(createFloatList2), "IT WORKS!"); System.out.println(table.get(createFloatList2)); createFloatList2.add(1f); System.out.println(table.get(createFloatList2)); createFloatList2.remove(3); System.out.println(table.get(createFloatList2)); } public static ArrayList createFloatList() { ArrayList floatee = new ArrayList<>(); floatee.add(34.234f); floatee.add(new Float(33)); floatee.add(null); return floatee; } Output: IT WORKS! null IT WORKS! 

Claro que es posible. Supongo que el problema en tu put . Pruebe obtener la clave para bigram, increméntela, elimine la entrada con este bigram e inserte el valor actualizado

Por favor, compruebe debajo de mi código para entender si la clave es ArrayList en Mapa y cómo JVM lo hará para las entradas: aquí escribo hashCode y es igual al método para la clase TesthashCodeEquals.

 package com.msq; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; class TesthashCodeEquals { private int a; private int b; public TesthashCodeEquals() { // TODO Auto-generated constructor stub } public TesthashCodeEquals(int a, int b) { super(); this.a = a; this.b = b; } public int getA() { return a; } public void setA(int a) { this.a = a; } public int getB() { return b; } public void setB(int b) { this.b = b; } public int hashCode() { return this.a + this.b; } public boolean equals(Object o) { if (o instanceof TesthashCodeEquals && o != null) { TesthashCodeEquals c = (TesthashCodeEquals) o; return ((this.a == ca) && (this.b == cb)); } else return false; } } public class HasCodeEquals { public static void main(String[] args) { Map, String> m = new HashMap<>(); List list1=new ArrayList<>(); list1.add(new TesthashCodeEquals(1, 2)); list1.add(new TesthashCodeEquals(3, 4)); List list2=new ArrayList<>(); list2.add(new TesthashCodeEquals(10, 20)); list2.add(new TesthashCodeEquals(30, 40)); List list3=new ArrayList<>(); list3.add(new TesthashCodeEquals(1, 2)); list3.add(new TesthashCodeEquals(3, 4)); m.put(list1, "List1"); m.put(list2, "List2"); m.put(list3, "List3"); for(Map.Entry,String> entry:m.entrySet()){ for(TesthashCodeEquals t:entry.getKey()){ System.out.print("value of a: "+t.getA()+", value of b: "+t.getB()+", map value is:"+entry.getValue() ); System.out.println(); } System.out.println("######################"); } } } 

.

 output: value of a: 10, value of b: 20, map value is:List2 value of a: 30, value of b: 40, map value is:List2 ###################### value of a: 1, value of b: 2, map value is:List3 value of a: 3, value of b: 4, map value is:List3 ###################### 

entonces esto verificará el número de objetos en Lista y los valores de valriabe en el objeto. si el número de objetos es el mismo y los valores de las variables de instancia también son iguales, considerará la clave duplicada y anulará la clave.

ahora si cambio solo el valor de objeto en list3

list3.add (nuevo TesthashCodeEquals (2, 2));

entonces se imprimirá:

  output value of a: 2, value of b: 2, map value is:List3 value of a: 3, value of b: 4, map value is:List3 ###################### value of a: 10, value of b: 20, map value is:List2 value of a: 30, value of b: 40, map value is:List2 ###################### value of a: 1, value of b: 2, map value is:List1 value of a: 3, value of b: 4, map value is:List1 ###################### 

para que siempre compruebe el número de objetos en Lista y el valor de la variable de instancia del objeto.

Gracias

ArrayList.equals() se hereda de java.lang.Object ; por lo tanto, equals() en ArrayList es independiente del contenido de la lista.

Si desea utilizar una ArrayList como clave de mapa, deberá anular equals() y hashcode() para hacer que dos arrays con el mismo contenido en el mismo orden devuelvan verdadero en una llamada a equals() y devolver el mismo código hash en una llamada a hashcode() .

¿Hay alguna razón particular por la que tiene que usar una ArrayList en lugar de decir una cadena simple como la clave?

edit: ignórame, como señaló Joachim Sauer a continuación, estoy tan equivocado que ni siquiera es gracioso.