2012-04-02 133 views
12

是否可以添加ArrayList作爲HashMap的鍵。我想保持bigrams的頻率計數。二元論是關鍵,其價值在於其頻率。ArrayList作爲Hashmap中的鍵

對於像「他是」這樣的兩個bigrams,我爲它創建一個ArrayList並將其插入到HashMap中。但我沒有得到正確的輸出。

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

回答

2

爲什麼你不能使用這樣的事情:

class Bigram{ 
    private String firstItem; 
    private String secondItem; 

    <getters/setters> 

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

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

,而不是使用動態收集物品數量有限(二)。

+1

我甚至會忽略setter並使其不可變。施工後可能沒有理由改變該類的對象。 – 2012-04-02 09:15:44

+0

+1 - 事實上,這可能會節省**空間,因爲Bigram類不會有32位長度字段的開銷。 – 2012-04-02 09:17:51

-3

ArrayList.equals()繼承自java.lang.Object - 因此ArrayList上的equals()獨立於列表的內容。

如果你想使用ArrayList作爲地圖的關鍵,你需要重寫equals()hashcode()爲了做兩個的ArrayList,在相同的順序返回相同的內容真正在通話過程中equals()並返回相同的哈希碼致電hashcode()

是否有什麼特別的原因讓你使用ArrayList而不是說簡單的String作爲鍵?

編輯:忽略我,正如Joachim Sauer指出的那樣,我錯了,它甚至不好笑。

+5

實際上'ArrayList'使用'AbstractList.equals()',它的實現恰到好處。實際上,每個正確的'List'實現都需要符合'equals()'和'hashCode()'實現。 – 2012-04-02 09:14:32

+1

啊,謝謝你的糾正。我只是做了ArrayList源碼的快速掃描,並沒有打算繼續前進 - 史詩般的失敗。 – mcfinnigan 2012-04-02 09:24:03

+0

提示:在Eclipse中有Ctrl-O打開一個輪廓對話框,輸入'equals',看到沒有定義,再次按下Ctrl-O來查看繼承的成員,並看到實際上有4個繼承的成員('Object',''集合「,」列表「和」抽象列表「)。我確信其他IDE中也有類似的快捷方式。 – 2012-04-02 09:26:02

18

是的,你可以有ArrayList S作爲一個哈希映射中的鑰匙,但它是一個非常糟糕的主意,因爲它們是可變

如果您以任何方式(或其任何元素)更改ArrayList,則映射基本上會丟失,因爲密鑰與插入時不會有相同的hashCode

經驗法則是僅使用不可變數據類型作爲哈希映射中的鍵。作爲由Alex Stybaev建議,你可能想創建一個Bigram類是這樣的:

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

除了它是可變的,如果這是他真正在使用的,那麼引入一個'Bigram'類可能是一個好主意。 – 2012-04-02 09:15:05

1

試試這個,這將工作。

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

這個問題是,我不能夠**清單**,你可以給我一個想法嗎?或者我應該爲它啓動另一個線程。 – thetna 2012-04-02 09:19:37

+0

使用此功能,您可以將任何類型的列表傳遞給地圖。列表可以是字符串,整數或用戶定義的對象。 – vikiiii 2012-04-02 09:27:39

2

the documentation

注意:如果使用可變對象作爲地圖 按鍵很大,一定要小心。如果對象的值爲 ,則會以影響equals比較的方式更改,而 對象是地圖中的鍵時未指定地圖的行爲。此禁令的一個特例是它不允許映射將其自身作爲關鍵字。雖然允許地圖將自己作爲一個值包含在內的地圖是 ,但要特別小心的是 建議:equalshashCode方法不再是在此類地圖上定義的 。

您,當您使用可變對象作爲的hashCodeequals着想鍵照顧。

底線是最好使用不可變對象作爲鍵。

0

確實有可能。我想你的put這個問題。嘗試使用bigram獲取密鑰,增加它,使用此bigram刪除條目並插入更新後的值

-1

請檢查以下代碼以瞭解密鑰是否是Map中的ArrayList以及JVM如何處理輸入: hashCode和TesthashCodeEquals類的equals方法。

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

所以這將檢查List中的對象的數量和對象中的valriabe的值。如果對象的數量相同,並且實例變量的值也相同,那麼它將考慮重複鍵並覆蓋該鍵。

現在如果我改變對象的唯一的值上項目list3

list3.add

(新TesthashCodeEquals(2,2));

那麼它會打印:

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

,使其始終檢查清單對象的數目和對象的實例變量的值。

謝謝

1

我想出了這個解決方案。顯然,在所有情況下都不可用,例如,在步進hashcodes int容量或list.clone()併發症時(如果輸入列表被更改,鍵保持與預期相同,但是當List的項可變時,克隆列表與它的項目具有相同的引用,這會導致密鑰本身的改變)。

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

你看我如何測試它,出於某種原因,我仍然發現解決方案不可靠。它有效嗎? – Javo 2016-05-27 07:41:38