Java中的多值哈希表

26

在哈希表中,是否可能为相同的键拥有多个值?如果不行,你能推荐任何可用的类或接口吗?


1
真是太棒了,6个人基本上都在复制和粘贴同样的答案。叹气 - Mark Renouf
10
太棒了,有6个人独立得出了相同的答案。耶! - Michael Myers
我敢打赌这是一个重复的问题,如果有人愿意查看而不是仅仅发一个显而易见的帖子。 - Tom Hawtin - tackline
我确实看过了,但是我没有看到任何明显的重复项。还有其他人想试试吗? - Michael Myers
我也进行了搜索,但没有找到。如果存在重复项,那么它的命名/标记方式可能相当晦涩。 - Jonik
显示剩余2条评论
13个回答

21

不是的。这就是哈希表的设计思想。

但是,你可以使用Map<YourKeyObject, List<YourValueObject>>自己实现,并编写一些实用方法来创建列表(如果不存在),或者使用Google Collections中的Multimap

示例:

String key = "hello";
Multimap<String, Integer> myMap = HashMultimap.create();
myMap.put(key, 1);
myMap.put(key, 5000);
System.out.println(myMap.get(key)); // prints either "[1, 5000]" or "[5000, 1]"
myMap = ArrayListMultimap.create();
myMap.put(key, 1);
myMap.put(key, 5000);
System.out.println(myMap.get(key)); // always prints "[1, 5000]"

请注意,Multimap不是自制解决方案的完全等价物; Hashtable同步其所有方法,而Multimap没有这样的保证。这意味着如果您在多个线程上使用Multimap,可能会遇到问题。如果您只在一个线程上使用映射,则不会有任何区别(并且您应该使用HashMap而不是Hashtable)。

有趣的是,Multimap 在底层似乎只是执行 Map<YourKeyObject, List<YourValueObject>>。 - matt b
是的,基本上只是将不重要的细节抽象化。 - Michael Myers

11

哈希表的值是对象,因此您可以存储列表。


是的,您可以存储一个列表,但您不能只是添加一个项目到它,您必须获取对该列表的引用,然后将其添加到列表中。 - mnuzzo

9
在哈希表中,使用键值对来存储信息。在Java中,Hashtable类接受单个键的单个值。以下是尝试将多个值关联到单个键的示例:
Hashtable<String, String> ht = new Hashtable<String, String>();

ht.put("Answer", "42");
ht.put("Hello", "World");    // First value association for "Hello" key.
ht.put("Hello", "Mom");      // Second value association for "Hello" key.

for (Map.Entry<String, String> e : ht.entrySet()) {
  System.out.println(e);
}

试图将多个值("World""Mom")包含到单个键("Hello")中,我们最终得到在 Hashtable 中打印条目的以下结果:
Answer=42
Hello=Mom

""Hello""World"的键值对不在Hashtable中,只有第二个"Hello""Mom"条目在Hashtable中。这表明在Hashtable中不能将多个值与单个键关联。"
这里真正需要的是一个多重映射,它允许将多个值与单个键关联。
多重映射的一种实现是Multimap,来自Google Collections
Multimap<String, String> mm = HashMultimap.create();

mm.put("Answer", "42");
mm.put("Hello", "World");
mm.put("Hello", "Mom");

for (Map.Entry<String, String> e : mm.entries()) {
  System.out.println(e);
}

这与上面使用Hashtable的示例类似,但行为有很大不同——Multimap允许将多个值与单个键相关联。执行上述代码的结果如下:
Answer=42
Hello=Mom
Hello=World

正如所见,对于"Hello"键而言,与之相关联的值有"Mom"和"World"。与Hashtable不同的是,它不会丢弃一个值并将其替换为另一个值。Multimap可以保存每个键对应的多个值。

1
仅翻译文本内容:不是为了原创性,而是为了示例的清晰度和彻底性 :) - Jonik

7

不是再给出另一个多映射答案,我会问为什么你想要这样做?

这些多个值是否相关联?如果是,那么最好创建一个数据结构来容纳它们。如果不是,那么也许使用单独的映射更合适。

你是否将它们放在一起,以便可以根据键迭代它们?您可能需要寻找替代索引数据结构,例如SkipList。


1
重视大局是一个好习惯,我总是忘记这一点。 - Michael Myers
哦,你会推荐哪种SkipList实现方式呢? 显然JDK没有提供这个(如果我说错了请纠正我)。 - Jonik

5
只需自己制作:
Map<Object, List<Object>> multiMap = new HashMap<Object, List<Object>>();

添加:

  public void add(String key, Object o) {
    List<Object> list;
    if (multiMap.containsKey(key)) {
      list = multiMap.get(key);
      list.add(o);
    } else {
      list = new ArrayList<Object>();
      list.add(o);
      multiMap.put(key, list);
    }
  }

没有外部库,这是正确的方法。但这正是我在答案中提到的样板文件;使用Google Collections,添加将只需要一行,简单地说:multiMap.put(key, o)。 - Jonik

5
正如其他人指出的那样,不行。相反,考虑使用一个Multimap,它可以将多个值映射到同一个键上。 Google Collections更新Guava)库包含了一种实现,可能是你最好的选择。 编辑:当然,你可以像Eric建议那样,在你的Hashtable(或Map,更普遍地说)中将Collection作为一个值存储,但这意味着你自己编写不必要的样板代码。当使用像Google Collections这样的库时,它会为您处理低级的“管道”问题。查看这个很好的例子,了解如何通过使用Multimap而不是普通Java Collections类来简化代码。

4

没有任何答案表明我首先会怎么做。

我在面向对象编程方面获得的最大提升是当我决定在似乎稍微有用的情况下总是创建另一个类时——这就是我从这种模式中学到的东西之一。

几乎所有时候,我发现我正在尝试放入哈希表中的对象之间存在关系。往往情况下,有空间可以创建一个类——甚至可能只需要一个或两个方法。

事实上,我经常发现我甚至不需要一个 HashMap 类型的结构——一个简单的 HashSet 就足够了。

你存储为主键的项可以成为一个新对象的标识符——因此,你可以创建 equals 和 hash 方法只引用那个对象(eclipse 可以轻松地为你创建 equals 和 hash 方法)。这样,新对象将完全像原始对象一样保存、排序和检索,然后使用属性来存储其他项。

大多数时候,当我这样做时,我发现还有一些方法也适合放在那里,然后在我意识到应该一直存在一个完整的对象之前,我就把一堆垃圾清除出我的代码。

为了使它更像“初步步骤”,我经常创建包含在原始类中的新类——有时甚至在方法中包含该类,如果以那种方式进行作用域限定是有意义的——然后随着它变得更加清晰应该成为一级类时,我就将其移动。


2

简单易懂。不要使用 Hashtable<Key, Value>,而是使用 Hashtable<Key, Vector<Value>>


只是一个小备注:HashMap默认情况下不是同步的,那么为什么要在其中使用Vector而不是List实现呢? - akarnokd
好的,原始问题是关于HashTables而不是HashMaps。 - Michael Myers
2
是的,这就是为什么这个答案使用HashMap和Vector(而不是Hashtable和Vector或HashMap和ArrayList等)令人好奇的原因。在我看来,从教学上讲,使用Map和List会更好。 :) - Jonik

2

2

1
在阅读了其他人的答案之后,我不得不得出这样一个结论:我的支持 Google Collections API 并不是非常原创。 (它是一项很好的工作,不是吗?) - bendin
我只是出于个性化的考虑链接到了commons-collections :-P (实际上这是第一个搜索结果,而且我懒得再找了)。 - Mark Renouf

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