2012-04-02 19 views
12

Czy byłoby możliwe dodanie ArrayList jako klucza do HashMap. Chciałbym utrzymać liczbę częstotliwości bigrams. Bigram jest kluczem, a wartość jest jego częstotliwością.ArrayList jako klucz w Hashmap

Dla każdego z bigrams takich jak "on jest", tworzę dla niego ArrayList i wstawiam go do HashMap. Ale nie otrzymuję prawidłowego wyniku.

public HashMap<ArrayList<String>, Integer> getBigramMap (String word1,String word2){ 
    HashMap<ArrayList<String>, Integer> hm = new HashMap<ArrayList<String>, Integer>(); 
    ArrayList<String> arrList1 = new ArrayList<String>(); 
    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<String> getBigram(String word1, String word2){ 
    ArrayList<String> arrList2 = new ArrayList<String>(); 
    arrList2.add(word1); 
    arrList2.add(word2); 
    return arrList2; 
} 

Odpowiedz

2

Dlaczego nie można użyć coś takiego:

class Bigram{ 
    private String firstItem; 
    private String secondItem; 

    <getters/setters> 

    @Override 
    public int hashCode(){ 
     ... 
    } 

    @Override 
    public boolean equals(){ 
     ... 
    } 
} 

zamiast używania kolekcji dynamiczny dla ograniczonej liczby elementów (dwa).

+1

Zostawiłbym nawet seterów i uczynił go niezmiennym. Prawdopodobnie nie ma powodu, aby zmieniać obiekty tej klasy po zakończeniu budowy. –

+0

+1 - w rzeczywistości, to prawdopodobnie ** oszczędza ** spację, ponieważ klasa Bigram nie będzie miała nakładu 32-bitowego pola "długość". –

-3

ArrayList.equals() jest dziedziczona po java.lang.Object - dlatego equals() na ArrayList jest niezależna od zawartości listy.

Jeśli chcesz użyć ArrayList jako klucz mapy, trzeba będzie zastąpić equals() i hashcode() w celu uczynienia dwóch arraylists o tej samej treści w tym samym zwrotu zamówienia prawdziwego na wezwanie do equals() i powrót tą samą hashcode pod numerem telefonu hashcode().

Czy istnieje jakiś szczególny powód, dla którego użytkownik ma używać tablicy ArrayList zamiast zwykłego prostego łańcucha jako klucza?

edytuj: Zignoruj ​​mnie, jak zauważył Joachim Sauer poniżej, tak się mylę, że to nawet nie jest zabawne.

+5

W rzeczywistości 'ArrayList' używa' AbstractList.equals() ', która jest zaimplementowana w sam raz. W rzeczywistości każda poprawna implementacja 'List' musi spełniać wymagania implementacji' equals() 'i' hashCode() '. –

+1

Ah, dzięki za poprawkę. Właśnie zrobiłem szybkie skanowanie źródła ArrayList i nie zadałem sobie trudu, aby pójść dalej - epickiej awarii z mojej strony. – mcfinnigan

+0

Podpowiedź: w Eclipse jest Ctrl-O, aby otworzyć okno dialogowe konspektu, wpisać 'equals', nie widzieć definicji, ponownie nacisnąć Ctrl-O, aby zobaczyć również dziedziczone elementy i zobaczyć, że istnieją 4 dziedziczone (' Object', ' Kolekcja "," Lista "i" Lista "). Jestem pewien, że istnieją podobne skróty również w innych IDE. –

18

Tak można mieć ArrayList s jako klucze w mapie mieszania, ale jest to bardzo zły pomysł ponieważ są one zmienny.

Jeśli zmienisz ArrayList w jakikolwiek sposób (lub którykolwiek z jego elementów), mapowanie zostanie zasadniczo utracone, ponieważ klucz nie będzie miał tego samego hashCode, jak miało to miejsce, gdy został wstawiony.

Zasadą jest używanie tylko niezmiennych typów danych jako kluczy na mapie skrótów. Jak sugeruje Alex Stybaev, prawdopodobnie chcesz utworzyć klasę Bigram tak:

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); 
    } 
} 
+0

Oprócz tego, że jest zmienny, prawdopodobnie dobrze jest po prostu wprowadzić klasę "Bigramu", jeśli tak naprawdę on pracuje. –

1

Spróbuj tego, to będzie działać.

public Map<List, Integer> getBigramMap (String word1,String word2){ 
    Map<List,Integer> hm = new HashMap<List, Integer>(); 
    List<String> arrList1 = new ArrayList<String>(); 
    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; 
} 
+0

Problem polega na tym, że nie jestem w stanie sparametryzować ** listy **. Czy możesz mi również powiedzieć, w jaki sposób? Lub powinienem rozpocząć dla niego inny wątek. – thetna

+0

Używając tego, możesz przekazać listę dowolnego rodzaju na mapę.Lista może składać się z łańcucha, liczby całkowitej lub obiektu zdefiniowanego przez użytkownika. – vikiiii

2

Od the documentation:

Uwaga: wielki należy zachować ostrożność, jeśli Zmienne przedmioty są używane jako map kluczy. Zachowanie mapy nie jest określone, jeśli wartość obiektu jest zmieniona w sposób, który wpływa na porównania equals, podczas gdy obiekt jest kluczem na mapie. Szczególnym przypadkiem tego zakazu jest to, że nie jest dopuszczalne, aby mapa zawierała się jako klucz. Mimo że dopuszcza się, aby mapa zawierała się jako wartość, zaleca się zachowanie szczególnej ostrożności: equals i nie są już dobrze zdefiniowane na takiej mapie.

Trzeba dbać kiedy używasz zmienny obiektów jako klucze dla dobra hashCode i equals.

Najważniejsze jest to, że lepiej używać niezmiennych obiektów jako klawiszy.

0

Pewnie, że to możliwe. Przypuszczam, że problem występuje w twoim put. Spróbuj uzyskać klucz do bigramu, zwiększ go, usuń wpis z tym bigramem i wstaw wartość zaktualizowaną

-1

Proszę sprawdzić poniżej mój kod, aby zrozumieć, czy klucz jest ArrayList w Mapie i jak JVM zrobi to dla wejść: tutaj piszę metoda hashCode i equals dla klasy 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 == c.a) && (this.b == c.b)); 

     } else 
      return false; 
    } 
} 

public class HasCodeEquals { 
    public static void main(String[] args) { 

     Map<List<TesthashCodeEquals>, String> m = new HashMap<>(); 

     List<TesthashCodeEquals> list1=new ArrayList<>(); 
     list1.add(new TesthashCodeEquals(1, 2)); 
     list1.add(new TesthashCodeEquals(3, 4)); 

     List<TesthashCodeEquals> list2=new ArrayList<>(); 
     list2.add(new TesthashCodeEquals(10, 20)); 
     list2.add(new TesthashCodeEquals(30, 40)); 


     List<TesthashCodeEquals> 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<List<TesthashCodeEquals>,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 
###################### 

to sprawdzi liczbę obiektów na liście i wartości valriabe w obiekcie. jeśli liczba obiektów jest taka sama, a wartości zmiennych instancji również są takie same, to rozważy duplikat klucza i zastąpi klucz.

teraz jeśli mogę zmienić tylko wartość obiektu na lista3

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

następnie zostanie wydrukowana:

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 
###################### 

tak, że zawsze sprawdzić liczbę obiektów na liście, a wartość zmiennej instancji obiektu.

dziękuję

1

Wymyśliłem to rozwiązanie. Oczywiście nie jest to użyteczne we wszystkich przypadkach, na przykład przy zwiększaniu wydajności węzłów int lub komplikacji list.clone (jeśli lista wejściowa zostanie zmieniona, klucz pozostanie taki sam, jak zamierzony, ale gdy elementy listy są zmienne, sklonowane lista ma takie samo odniesienie do jej elementów, co spowodowałoby zmianę samego klucza).

import java.util.ArrayList; 

public class ListKey<T> { 
    private ArrayList<T> list; 

    public ListKey(ArrayList<T> list) { 
     this.list = (ArrayList<T>) 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<Float> createFloatList = createFloatList(); 
     ArrayList<Float> createFloatList2 = createFloatList(); 

     Hashtable<ListKey<Float>, 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<Float> createFloatList() { 
     ArrayList<Float> floatee = new ArrayList<>(); 
     floatee.add(34.234f); 
     floatee.add(new Float(33)); 
     floatee.add(null); 

     return floatee; 
    } 

Output: 
IT WORKS! 
null 
IT WORKS! 
+0

Widzisz, jak to przetestowałem, z jakiegoś powodu nadal uważam, że rozwiązanie jest niewiarygodne. Czy to jest ważne? – Javo