【问题标题】:Java - Using HashMap with List<> as KeyJava - 使用 HashMap 和 List<> 作为键
【发布时间】:2017-06-30 03:26:39
【问题描述】:

我正在尝试将 HashMap 和 Hastable 与对象列表一起用作键。 请看下面我的代码的简化版本,它不起作用。 当我调试这段代码时,我希望 TestMap4 对象中有 3 个项目,但只有 1 个。

List<String> lst = new ArrayList<>();
lst.add("Hello");
lst.add("World");

Map<List<String>, Integer> testMap4 = new HashMap<List<String>, Integer>();
testMap4.put(lst, 1);
testMap4.put(lst, 2);
testMap4.put(lst, 5);

当我将一个新项目放入 HashMap 对象时会发生什么?为什么它不起作用?

我在下面的这个新示例中获得了相同的结果。 (每个 List 都包含相同的 2 个字符串)

List<String> lst = new ArrayList<>();
lst.add("Hello");
lst.add("World");

List<String> lst2 = new ArrayList<>();
lst2.add("Hello");
lst2.add("World");

List<String> lst3 = new ArrayList<>();
lst3.add("Hello");
lst3.add("World");

Map<List<String>, Integer> testMap4 = new HashMap<List<String>, Integer>();
testMap4.put(lst,1);
testMap4.put(lst2,2);
testMap4.put(lst3,5);

如果我只修改 2 个字符串的 1 个字符,这没关系

【问题讨论】:

  • 您误解了地图的键/值部分:tutorialspoint.com/java/java_hashmap_class.htm。 Map 只能有 1 个值,即与 1 个唯一键相关联。您使用相同的键放置值,因此每次都覆盖现有值
  • 数字 1、2 和 5 的含义是什么?您是否试图将列表(同一个列表)与键 1,2 和 5 放在一起。如果是这样,您的键和值是错误的
  • 有没有办法修改每个项目列表的哈希码以将其标识为唯一?

标签: java arraylist hashmap


【解决方案1】:

你不懂HashMap的概念。

您的问题是您每次都使用相同的密钥。

testMap4.put(lst, 1);     // <----same key, different value
testMap4.put(lst, 2);     // <----same key, different value
testMap4.put(lst, 5);     // <----same key, different value

Hashmap 中,存储在Hashmap 中的每个值,都有一个key 与该特定值一起保存,并且对于存储在Hashmap 中的每个值唯一

关于HashMap的要点:

1- HashMap 包含基于键的值。

2- 它只包含独特的元素。

3- 它可能有一个空键和多个空值。

4- 它没有秩序。

示例

 HashMap<Integer,String> hm = new HashMap<>();  

其次,如果任何列表在插入映射后被修改,则使用可变对象(List&lt;&gt;)作为键会导致未定义的行为。仅当条目首次插入映射时,才会根据 List 的合约(参见 Javadoc)计算哈希码。对列表内容的更改将更改哈希码,您将无法再找到该条目。

使用List&lt;&gt;(或任何可变对象)作为HashMap&lt;&gt; 中的键是一个非常糟糕的主意™

【讨论】:

    【解决方案2】:

    它不起作用,因为您每次都使用相同的键来存储不同的值,因为所有值都映射到相同的键,而 hashmap 只存储最后一个值,因为这个值会覆盖以前的值。

    【讨论】:

      【解决方案3】:

      HashMap 对您放入 HashMap 的键对象调用 hashCode() 方法。

      由于您没有为您使用的键类(在您的情况下为List&lt;&gt;)覆盖它,它调用java.lang.Object 上的hashCode() 方法,该方法返回一个唯一的对象ID。

      当您将同一个对象放入 Map 三次时,它就是您连续三次放入相同的键。

      List<String> lst1 = new ArrayList<>();
      lst.add("Hello");
      lst.add("World");
      List<String> lst2 = new ArrayList<>();
      List<String> lst3 = new ArrayList<>();
      
      Map<List<String>, Integer> testMap4 = new HashMap<List<String>, Integer>();
      testMap4.put(lst1, 1);
      testMap4.put(lst2, 2);
      testMap4.put(lst3, 5);
      

      将在您的地图中为您提供三个条目。

      如果您需要在列表内容上使用 HashCode 以用作 HashMap 键,请查看:

      static int java.util.Objects.hash(Object... values)
      static boolean java.util.Arrays.equals(Object[] a, Object[] a2)
      

      不要忘记您总是必须重写两个方法。 hashCode() equals()。如果您只覆盖其中一个,您将立即死亡! ;)

      【讨论】:

      • 我扩展了 ArrayList 类并重写了 hashCode() 方法,如下所示:@Override public int hashCode() { return UUID.randomUUID().hashCode(); } 在我的情况下似乎可行。
      • 所以这绝对是错误的!!如果您返回一个随机数,则put(o, x) 上的 hashCode 将不同于 get(o) 上的 hashCode,这意味着您永远将能够再次从 HashMap 中检索您的值!如果您将一个字符串作为键两次放入 Map 中,则第二个 put 也将覆盖第一个 put!这就是地图的工作原理!
      • 我查看了 AbstractList 的 equals 和 compare ,它们已经实现了基于 List 的 contents 给出 hashCode 和 equals。因此,除非您想要检索相同的值,无论您如何修改列表将其放入地图中,都无需覆盖某些内容。在这种情况下,我将覆盖 hashCode 和 Compare 以调用 Objects 等于和比较。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2013-04-15
      • 1970-01-01
      • 1970-01-01
      • 2017-05-24
      • 2017-07-06
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多