ArrayList作为HashMap中的键

时间:2012-04-02 09:07:42

标签: java hashmap nlp

是否可以添加ArrayList作为HashMap的关键字。我想保留双字母的频率数。二元组是关键,价值就是它的频率。

对于像“他是”这样的每个双子星,我为它创建一个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;
}

9 个答案:

答案 0 :(得分:26)

是的,您可以将ArrayList作为哈希映射中的键,但它是非常糟糕的主意,因为它们可变

如果以任何方式(或其任何元素)更改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);
    }
}

答案 1 :(得分:2)

为什么你不能使用这样的东西:

class Bigram{
    private String firstItem;
    private String secondItem;

    <getters/setters>

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

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

而不是将动态集合用于有限数量的项目(两个)。

答案 2 :(得分:2)

来自the documentation

  

注意:如果将可变对象用作地图,则必须非常小心        键。如果对象的值是,则不指定映射的行为        以一种影响equals比较的方式改变了        对象是地图中的关键。这个禁令的一个特例就是它        不允许地图将自己包含为关键字。虽然它是        允许地图将自己包含为一个值,极其谨慎        建议:不再使用equalshashCode方法        在这样的地图上明确定义。

为了hashCodeequals而使用可变对象作为键,您必须小心。

最重要的是,最好将不可变对象用作键。

答案 3 :(得分: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;
}

答案 4 :(得分:1)

我已经提出了这个解决方案。它显然在所有情况下都不可用,例如在步进哈希码int容量或list.clone()并发症时(如果输入列表被更改,键保持与预期相同,但当List的项是可变的时,克隆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!

答案 5 :(得分:0)

当然有可能。我想你put中的问题。尝试获取bigram的密钥,递增它,使用此bigram删除条目并插入更新的值

答案 6 :(得分:0)

Array不同,List可以用作HashMap的键,但这不是一个好主意,因为我们应该始终尝试使用不可变的对象作为键< / strong>。

在许多情况下,

.toString()方法获得String表示形式是一个不错的关键选择,因为String是一个固定的对象,可以完美地代表数组或列表。

答案 7 :(得分:-1)

请检查下面我的代码,以了解key是否是Map中的ArrayList以及JVM将如何为输入执行此操作: 在这里,我为TesthashCodeEquals类编写hashCode和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(new 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
######################

这样它总是检查List中的对象数和object的实例变量的值。

感谢

答案 8 :(得分:-3)

ArrayList.equals()继承自java.lang.Object - 因此,ArrayList上的equals()独立于列表的内容。

如果要将ArrayList用作地图键,则需要覆盖equals()hashcode(),以使两个具有相同内容的相同内容的arraylists在通话时返回true到equals()并在调用hashcode()时返回相同的哈希码。

是否有任何特殊原因使 使用ArrayList而不是简单的String作为键?

编辑:忽略我,正如Joachim Sauer在下面指出的那样,我错了,甚至都不好笑。