String Vs Stringbuffer作为HashMap键

时间:2014-05-21 17:49:20

标签: java string hashmap stringbuilder stringbuffer

我试图理解为什么String和Stringbuilder / StringBuffer在用作Hashmap键时会被区别对待。让我用下面的插图让我更加困惑:

示例#1,使用String:

String s1 = new String("abc");
String s2 = new String("abc");
HashMap hm = new HashMap();
hm.put(s1, 1);
hm.put(s2, 2);
System.out.println(hm.size());

以上代码段打印'1'。

示例#2,使用StringBuilder(或StringBuffer):

StringBuilder sb1 = new StringBuilder("abc");
StringBuilder sb2 = new StringBuilder("abc");
HashMap hm = new HashMap();
hm.put(sb1, 1);
hm.put(sb2, 2);
System.out.println(hm.size());

以上代码段打印'2'。

有人可以解释为什么行为上的差异。

4 个答案:

答案 0 :(得分:3)

StringBuilder / Buffer不会覆盖hashCode和equals。这意味着对象的每个实例都应该是唯一的哈希码,并且它的值或状态无关紧要。您应该使用String作为密钥。

StringBuilder / Buffer也是可变的,用作HashMap的键通常不是一个好主意,因为在它下面存储值会导致在修改后无法访问该值。

http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/6-b14/java/lang/StringBuilder.java

答案 1 :(得分:2)

StringBuilder使用Object的默认hashcode()实现,而字符串则按地图键的值进行比较。

Map的工作方式(特别是HashMap)是它使用对象的hashcode,而不是类的内容。

请注意,您的地图上缺少参数化:

HashMap yourMap = new HashMap();
//Should be
Map<String, Integer> yourMap = new HashMap<>();

并且没有理由创建新的字符串对象而不是分配内部文字:

String s1 = "abc";

答案 2 :(得分:0)

我注意到你正在使用新的String(“abc”);这意味着你知道String a =“abc”和String b =“abc”是相同的。

所以,a == b返回true。和a.equals(b)返回true。

然而同样的事情对StringBuffers不起作用,因为它的equals没有考虑对象的值,只考虑它的hashcode。

如果查看StringBuffer,您会看到它使用了Object.equals,即

public boolean equals(Object obj) {
    return (this == obj);
}

String的等于是:

public boolean equals(Object anObject) {
    if (this == anObject) {
        return true;
    }
    if (anObject instanceof String) {
        String anotherString = (String) anObject;
        int n = value.length;
        if (n == anotherString.value.length) {
            char v1[] = value;
            char v2[] = anotherString.value;
            int i = 0;
            while (n-- != 0) {
                if (v1[i] != v2[i])
                        return false;
                i++;
            }
            return true;
        }
    }
    return false;
}

答案 3 :(得分:0)

要了解行为,了解Hashmaps的工作方式非常重要。 Hashmaps使用密钥对象返回的哈希码值(在本例中为String对象)将它们存储到称为存储桶的相应内存区域中。因此,在检索与其关联的值时,哈希映射需要找到存储密钥的隔离区,然后根据该密钥返回值。 Hashmaps从密钥的哈希码值中标识隔离专区。

现在,想象一下,如果我有两个具有相同哈希码值的对象,会发生什么?在这种情况下,hashmap首先需要知道两个对象是否相同,因为如果它们是,那么它将意味着地图中只有一个条目,并且与该键相关联的现有值将被替换为新值。但是,仅具有相同的哈希码并不意味着两个键都相等。因此,通过在关键对象上调用.equals()方法来确定相等性。如果.equals()返回true,则对象相等,在这种情况下,哈希映射需要更新现有条目的值。但是如果.equals()返回false呢?在这种情况下,两个对象都是不同的,应该作为单独的条目存储。因此,它们并排存放在同一隔间中。因此,在检索值时,输入键的哈希码用于到达存储它的隔离区,然后如果隔离专区包含多个对象,则检查输入键是否与隔离区中的每个对象相等并且如果匹配则返回相关值。

现在,让我们将上述理论应用于您拥有的代码。如果String对象的内容相等,则它们是相等的。按规则,如果两个对象相等,则应返回相同的哈希码。但请记住,相反并非如此。如果两个对象返回相同的哈希码,而不需要它们相等。这一开始似乎令人困惑,但你可以在几次迭代中克服它。具有相同内容的两个字符串是相等的并且返回相同的哈希码,即使它们是物理上不同的对象,因此当在hashmap中用作键时将始终映射到相同的条目。因此行为。

String类重写默认的equals()方法,该方法表示如果两个对象具有相同的引用,则它们相等,而依赖于内容的引用是相等的。它可以这样做,因为字符串是不可变的。但是,StringBuffer不会这样做。它仍然依赖于参考平等。