【问题标题】:HashMap of ArrayList<String>, String is not working properly?ArrayList<String> 的 HashMap,String 不能正常工作?
【发布时间】:2013-01-27 08:48:48
【问题描述】:

所以,我想制作一个字符串列表到字符串的映射,但我无法让它正常工作:

这是我完成的所有代码,在我找出原因之前,我无法继续:

Map<List<String>, String> test = new HashMap<List<String>, String>();
test.put( new ArrayList<String>(), "s1");
test.put( new ArrayList<String>(), "s2");
test.put( new ArrayList<String>(), "s3");

System.out.println(test.size());

我得到 1,应该是 3! 为什么当我为 3 个单独的对象进行 3 次调用时只添加了一个对象?我知道不小心将同一个对象添加到集合中的危险,但我专门为每个 put 创建了一个新的 ArrayList,从而创建了一个全新的对象。

那么为什么 Map 中只有一个对象呢? 谢谢!

【问题讨论】:

  • 你不应该在 HashMap 中使用可变键。

标签: java string arraylist hashmap


【解决方案1】:

ArrayList#hashCode() 为所有这些返回相同的值。如果您查看它的来源:http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/6-b14/java/util/AbstractList.java#AbstractList.hashCode%28%29

你可以看到,如果没有元素,或者所有相同的元素,hashCode 将是相同的。

修复它,它应该可以正常工作。使用 Map 或以一种或另一种方式更改 hashCode。

【讨论】:

  • 谢谢!如何更改 hashCode? TreeMap 能解决问题吗?
  • 那会很好用。您可以覆盖 ArrayList 并更改 hashCode 方法。例如,使其具有唯一的 ID,例如:创建时间、随机生成的数字等。
【解决方案2】:

试试这个:

Map<String,List<String>> test = new HashMap<String,List<String>>();
test.put("s1", new ArrayList<String>());
test.put("s2", new ArrayList<String>());
test.put("s3", new ArrayList<String>());

System.out.println(test.size());

请注意,映射是key-value 关系。出于这个原因,您可能也希望将String 用作,将ArrayList 用作,而不是相反。这样,如果添加 3 个不同的字符串,每个字符串都会有不同的哈希值(hashcode)。因此,您的Map 中将有 3 个不同的键。

还要注意:

public Object put(对象键, 对象值)将指定值与此映射中的指定键相关联。如果地图之前包含 此键的映射,旧值被替换

这就是您得到 1 而不是 3 的原因,因为您添加了相同的对象 new ArrayList&lt;String&gt;()

详细了解Class HashM 规范。

【讨论】:

    【解决方案3】:

    你用ArrayList作为key,试试

        System.out.println(new ArrayList<String>().equals(new ArrayList<String>()));
    

    打印出来

    true
    

    【讨论】:

    • hashCode 的一般约定是:如果两个对象根据 equals(Object) 方法相等,那么对这两个对象分别调用 hashCode 方法必须产生相同的整数结果。
    【解决方案4】:

    最好的方法是使用 String 对象作为键,使用 List 作为值。

    看看 java 文档是怎么说的 here

    如果你想添加 ArrayList 作为键,那么重写 equals 方法就足够了。

    here 的帖子提供了很好的见解。我放了一些我喜欢的帖子。

    覆盖两个 ArrayList 的等于:

     public boolean equals(List<String> one, List<String> two){
        if (one == null && two == null){
            return true;
        }
    
        if((one == null && two != null) 
          || one != null && two == null
          || one.size() != two.size()){
            return false;
        }
    
        //to avoid messing the order of the lists we will use a copy
        //as noted in comments by A. R. S.
        one = new ArrayList<String>(one); 
        two = new ArrayList<String>(two);   
    
        Collections.sort(one);
        Collections.sort(two);      
        return one.equals(two);
    }
    

    【讨论】:

    • 如何覆盖equals方法?
    • 但是再次确认一下,您真的需要 ArrayList 作为键吗?
    【解决方案5】:

    我不知道为什么你需要列表作为键和字符串作为值,但你基本上对每个 put 操作使用相同的“键”,因为哈希码对于任何空列表都是相同的。就像提到的其他答案一样,最好将列表切换为值,将字符串切换为键。如果列表发生变化,任何未来的 .get 尝试都将返回 null

       public static void main(String...args) {
           Map<List<String>, String> test = new HashMap<List<String>, String>();
           List<String> bob = new ArrayList<>();
           bob.add("asdf");
           test.put( new ArrayList<String>(), "s1");
           test.put( bob, "s2");
    
           System.out.println(test.size());
           System.out.println(test.get(bob));
       }
    

    输出

    2
    s2
    

    当一个新项目被添加时像

       public static void main(String...args) {
           Map<List<String>, String> test = new HashMap<List<String>, String>();
           List<String> bob = new ArrayList<>();
           bob.add("asdf");
           test.put( new ArrayList<String>(), "s1");
           test.put( bob, "s2");      
           bob.add("aabbcc");
    
           System.out.println(test.size());
           System.out.println(test.get(bob));
       }
    

    get 将不起作用,因为列表的哈希已更改。这种情况下的输出是:

    2
    null
    

    【讨论】:

      猜你喜欢
      • 2014-01-13
      • 1970-01-01
      • 2013-07-29
      • 1970-01-01
      • 1970-01-01
      • 2017-08-30
      • 2013-05-27
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多