【问题标题】:ArrayList as key in HashMapArrayList 作为 HashMap 中的键
【发布时间】:2012-04-15 22:42:27
【问题描述】:

是否可以添加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;
}

【问题讨论】:

    标签: java hashmap nlp


    【解决方案1】:

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

    class Bigram{
        private String firstItem;
        private String secondItem;
    
        <getters/setters>
    
        @Override
        public int hashCode(){
            ...
        }
    
        @Override 
        public boolean equals(){
            ...
        }
    }
    

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

    【讨论】:

    • 我什至会省略设置器并使其不可变。构造后可能没有理由更改该类的对象。
    • +1 - 事实上,这可能节省空间,因为 Bigram 类不会有 32 位 length 字段的开销。
    【解决方案2】:

    ArrayList.equals() 继承自 java.lang.Object - 因此 ArrayList 上的 equals() 与列表的内容无关。

    如果您想使用 ArrayList 作为映射键,则需要覆盖 equals()hashcode() 以使两个具有相同内容且顺序相同的数组列表在调用 equals() 时返回 true并在调用 hashcode() 时返回相同的哈希码。

    您有什么特殊原因使用 ArrayList 而不是简单的 String 作为键?

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

    【讨论】:

    • 其实ArrayList使用AbstractList.equals(),实现的恰到好处。事实上,每个正确的List 实现都需要有符合equals()hashCode() 的实现。
    • 啊,谢谢指正。我只是对 ArrayList 源进行了快速扫描,并没有费心继续往下走——我失败了。
    • 提示:在 Eclipse 中有 Ctrl-O 打开大纲对话框,输入equals,看不到定义,再次按 Ctrl-O 也看到继承的成员,看到实际上有 4 个继承那些(ObjectCollectionListAbstractList)。我确信其他 IDE 中也有类似的快捷方式。
    • 我使用 IDEA - ^N - 我很懒 :-)
    【解决方案3】:

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

    如果您以任何方式更改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);
        }
    }
    

    【讨论】:

    • 除了它是可变的之外,如果这是他实际使用的,那么引入 Bigram 类可能是个好主意。
    【解决方案4】:

    试试这个,这会奏效。

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

    【讨论】:

    • 问题在于,我无法对 List 进行参数化。您能否也给我一些想法?或者我应该为它启动另一个线程。
    • 使用这个,您可以将任何类型的列表传递给地图。列表可以是字符串、整数或用户定义的对象。
    【解决方案5】:

    来自the documentation

    注意:如果将可变对象用作映射,则必须非常小心 键。如果对象的值是,则不指定映射的行为 以影响 equals 比较的方式更改,而 object 是地图中的一个键。该禁令的一个特殊情况是 不允许地图包含自己作为键。虽然它是 允许地图将自身包含为一个值,请格外小心 建议:equalshashCode 方法不再适用 在这样的地图上明确定义。

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

    底线是最好使用不可变对象作为键。

    【讨论】:

      【解决方案6】:

      当然可以。我想你的put 中的问题。尝试获取二元组的键,增加它,使用这个二元组删除条目并插入更新的值

      【讨论】:

        【解决方案7】:

        请检查下面我的代码,以了解键是否为 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中的对象数量和对象实例变量的值。

        谢谢

        【讨论】:

          【解决方案8】:

          我想出了这个解决方案。它显然不是在所有情况下都可用,例如超过哈希码 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!
          

          【讨论】:

          • 你看我是如何测试它的,出于某种原因,我仍然发现该解决方案不可靠。有效吗?
          【解决方案9】:

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

          .toString() 方法获取字符串表示在许多情况下是一个不错的关键选择,因为String 是一个不可变对象,可以完美地代表数组或列表。

          【讨论】:

            猜你喜欢
            • 2011-10-10
            • 2016-06-04
            • 1970-01-01
            • 1970-01-01
            • 2017-08-14
            • 2014-02-26
            • 2016-02-13
            • 1970-01-01
            • 2021-12-21
            相关资源
            最近更新 更多