在HashMap中链接

时间:2012-08-06 04:26:25

标签: java hashmap chaining hash-collision

代码:

public static void main(String[] args) {
    Map<String,String> map= new HashMap<String,String>();
    map.put("a", "s");
    map.put("a", "v");

    System.out.println(map.get("a"));

}

现在,根据我的理解,由于put情况下的键值相同即a,因此必然会发生冲突,因此会发生链接。 [纠正我,如果我错了]。

现在,如果我想检索映射到键值a的所有值的列表,我该如何获取它?

现在我的println仅打印v

10 个答案:

答案 0 :(得分:4)

这与 collision chaining 无关:您将a的旧值替换为新值。

地图保留唯一键。当两个不同的键碰巧基于特定的散列函数获得相同的散列值时,将在散列数据结构中发生冲突/链接。或者在java中,您可以显式创建一个为hashCode()返回相同值的对象。

如果要为键创建多个值,则需要使用不同的数据结构/类。

答案 1 :(得分:2)

请参阅{docner for put

将指定的值与此映射中的指定键相关联(可选操作)。 如果地图以前包含该键的映射,则旧值将替换为指定的值。(当且仅当m.containsKey时,地图m包含键k的映射。 k)将返回true。)

当两个不同的键出现相同的hashcode而不是两个相同的键时,会发生冲突。

class StringKey {
String text;

public StringKey() {
    text = "";
}

public StringKey(String text) {
    this.text = text;
}

public String getText() {
    return text;
}

public void setText(String text) {
    this.text = text;
}

@Override
public int hashCode() {
    if (text != null) {
        text.substring(0, 1).hashCode();
    }
    return 0;
}

@Override
public boolean equals(Object o) {
    if (o instanceof StringKey) {
        return ((StringKey) o).getText().equals(this.getText());
    }
    return false;
}

public static void main(String[] args) {
    Map<StringKey, String> map = new HashMap<StringKey, String>();
    StringKey key1 = new StringKey("a");
    StringKey key2 = new StringKey("b");

    map.put(key1, "s");
    map.put(key2, "v");

    System.out.println(map.get(key1));
    System.out.println(key1.hashCode() + " " + key2.hashCode() + " " + key1.equals(key2));
}
}

输出

s
0 0 false

现在这会导致collision;但你无法从地图键和值的输出中解释这一点。

答案 2 :(得分:2)

像其他人已经建议的那样,对你的案子来说没有碰撞这样的事情。

这只是因为Hashmap只接受一个唯一的密钥。

但是,如果您希望密钥不唯一,则可以选择其他方式,例如Google Guava MultimapApache Multimap

使用Google lib的示例:

public class MutliMapTest {
public static void main(String... args) {
Multimap<String, String> myMultimap = ArrayListMultimap.create();

// Adding some key/value
myMultimap.put("Fruits", "Bannana");
myMultimap.put("Fruits", "Apple");
myMultimap.put("Fruits", "Pear");
myMultimap.put("Vegetables", "Carrot");

// Getting the size
int size = myMultimap.size();
System.out.println(size);  // 4

// Getting values
Collection<string> fruits = myMultimap.get("Fruits");
System.out.println(fruits); // [Bannana, Apple, Pear]

Collection<string> vegetables = myMultimap.get("Vegetables");
System.out.println(vegetables); // [Carrot]

// Iterating over entire Mutlimap
for(String value : myMultimap.values()) {
 System.out.println(value);
}

// Removing a single value
myMultimap.remove("Fruits","Pear");
System.out.println(myMultimap.get("Fruits")); // [Bannana, Pear]

// Remove all values for a key
myMultimap.removeAll("Fruits");
System.out.println(myMultimap.get("Fruits")); // [] (Empty Collection!)
}
}

答案 3 :(得分:1)

第二个put()只是覆盖了第一个put()所写的内容。没有链接。

答案 4 :(得分:1)

第二个put替换第一个put,因此Hashmap中只有一个值为“a”的值。

所以你的地图只包含

map.put("a", "v");

答案 5 :(得分:1)

  

现在,按照我的理解,既然是put的情况下的键值   是相同的,即a,碰撞必然发生,因此链接   发生。 [纠正我,如果我错了]。

你错了。那不是Map如何运作的。考虑使用Google的Guava库中的MultiMap

你可以随时自己动手:

Map<String, ArrayList<String>>();

答案 6 :(得分:1)

您必须按照以下方式制作HashMap

public static void main(String[] args) {
    HashMap<String, ArrayList<String>> map = new HashMap<String, ArrayList<String>>();
    if ( map.get("a") == null ){
        map.put("a", new ArrayList<String>());
    }

    ArrayList<String> innerList = map.get("a");
    innerList.add("s");
    innerList.add("v");

    map.put("a",innerList);

    System.out.println(map.get("a"));
}

答案 7 :(得分:1)

HashMaps中使用的哈希算法在第一时间是非常模糊的。在内部,HashMap只是一个带索引的数组。这里的索引通常称为'hashValue'。由于hashValue是数组中元素的索引,因此它必须小于HashMap本身的大小.HashMap的散列算法将密钥的哈希码转换为hashValue。这是Map存储Entry(键值对)的地方。

当一个元素放入Map时,它会从元素键的hashcode生成hashValue,并将Entry存储到该索引处的数组中,该索引只是hashValue。 现在,散列算法只能在一定程度上有效,也就是说我们永远无法保证为两个不同的密钥生成的hashValue总是不同的。在两种情况下可能相同: 1)键是相同的(如你的情况) 2)密钥不同,但为两个密钥生成的hashValue是相同的。

我们根本无法替换数组中hashValue位置的Entry值,因为这会违反第二个条件,这是非常有效的。这是equals()进入画面的地方。现在,HashMap检查新密钥与该索引的Entry中存在的密钥之间的相等性。如果两个键都相同则意味着替换,否则它就是碰撞,而HashMap使用适当的碰撞技术。

现在,如果您想要为特定键添加的所有值的列表,请考虑使用复合映射 HashMap<String, List<String>>

答案 8 :(得分:0)

您尝试放入HashMap的两个密钥都具有相同的HashCode。因此,第一个值会被覆盖,最终只会在HashMap中包含一个值。

您可以通过覆盖hashCode()方法将两个相似的对象放在同一个HashMap中。

答案 9 :(得分:0)

关于在使用HashMap时实际发生链接的进一步说明:

HashMap的Java实现将覆盖一个键或一个对象,具体取决于以下内容:

  1. 您将对象 foo 作为键,将哈希码X放入地图中
  2. 您将另一个具有相同哈希码X的对象 bar (作为密钥..) 进入地图
  3. 由于哈希值相同,算法需要放入 对象 bar 在已存储 foo 的同一索引上。然后它会参考 foo equals 方法,以确定它是否 bar foo (即 foo.next()将成为 bar )或覆盖 foo with bar:

    3.1。如果等于返回true, foo &amp; bar 是相同的对象,或者它们在语义上是相同的,并且将覆盖而不是链接。

    3.2。如果equals返回false, foo &amp; bar 被视为两个不同的实体,并将进行链接。如果您随后打印了HashMap,那么您将同时看到 foo bar