Java - 使用List<>作为HashMap的键

3

我正在尝试使用HashMap和Hashtable,将一个对象列表作为键。下面是我的代码的简化版本,但它不起作用。当我调试这段代码时,我希望在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对象中添加新项时,会发生什么?为什么它不起作用?使用下面的新示例,我得到了相同的结果。 (每个列表都包含相同的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);

如果我只修改了两个字符串中的一个字符,那就没问题了。

你误解了Map中的键/值部分:https://www.tutorialspoint.com/java/java_hashmap_class.htm。一个Map只能有一个与之关联的唯一键对应的值。你把相同键的值放在一起,这样每次都会覆盖现有的值。 - JohnnyAW
数字1、2和5的含义是什么?您是否试图将相同的列表放在键1、2和5上?如果是这样,那么您的键和值是颠倒的。 - Richard Tingle
有没有办法修改每个项目(Item)列表的哈希码,以将其标识为唯一的? - ManWithNoName
3个回答

6

您不理解 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中,每个存储在其中的值都有一个与该特定值保存的key,而且对于HashMap中存储的每个值来说,这个key都是唯一的。
关于HashMap的重要点如下:
1- HashMap基于键(key)来存储值。
2- 它只包含唯一的元素。
3- 它可以有一个null键和多个null值。
4- 它不维护顺序。 例子
 HashMap<Integer,String> hm = new HashMap<>();  

其次,使用可变对象(如List<>)作为键,如果任何列表在插入到映射之后被修改,则会导致未定义的行为。哈希码仅在将条目首次插入映射时根据List的约定计算(请参阅Javadoc)。列表内容的更改将更改哈希码,您将无法再找到该条目。
HashMap<>中使用List<>(或任何可变对象)作为键是一个非常糟糕的想法。

0

它不起作用是因为您每次使用相同的键来存储不同的值,导致所有值都映射到相同的键,并且哈希映射仅存储最后一个值,因为此值覆盖了先前的值。


0

HashMap 在将键对象放入其中时会调用 hashCode() 方法。

由于您没有为使用的键类(在您的情况下是 List<>)覆盖它,因此它会调用 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);

这将在您的Map中提供三个条目。

如果您需要对列表内容进行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(); } 在我的情况下似乎起作用了。 - ManWithNoName
所以这绝对是错误的!! 如果你返回一个随机数,那么在 put(o, x) 上的 hashCode 将与在 get(o) 上的 hashCode 不同,这意味着你永远无法再次从 HashMap 中检索到你的值!如果你将一个字符串作为键放入 Map 中两次,第二次放置也会覆盖第一次放置! 这就是 Maps 的工作原理! - Dirk Hoffmann
我查看了 AbstractList 中的 equals 和 compare,它们已经实现了基于 List 内容的 hashCode 和 equals,因此除非你想检索相同的值,无论你在放入 Map 之后如何修改列表,否则不需要覆盖任何内容。在这种情况下,我会重写 hashCode 和 Compare 来调用 Object 的 equals 和 compare。 - Dirk Hoffmann

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接