Java 字符串与字符串缓冲区作为HashMap键
我试图理解为什么字符串和Stringbuilder/StringBuffer在用作Hashmap键时被区别对待。让我用以下插图来澄清我的困惑: 示例#1,使用字符串:Java 字符串与字符串缓冲区作为HashMap键,java,string,hashmap,stringbuilder,stringbuffer,Java,String,Hashmap,Stringbuilder,Stringbuffer,我试图理解为什么字符串和Stringbuilder/StringBuffer在用作Hashmap键时被区别对待。让我用以下插图来澄清我的困惑: 示例#1,使用字符串: 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” 示
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):
上面的代码片段打印“2”
任何人都可以解释行为上的差异。StringBuilder使用
对象的默认hashcode()
实现,而字符串是通过映射键的值进行比较的
Map
的工作方式(特别是HashMap)是利用对象的hashcode
,而不是类的内容
注意:地图上缺少参数化:
HashMap yourMap = new HashMap();
//Should be
Map<String, Integer> yourMap = new HashMap<>();
StringBuilder/Buffer不重写hashCode和equals。这意味着对象的每个实例都应该是唯一的哈希代码,其值或状态无关紧要。您应该使用字符串作为键
StringBuilder/Buffer也是可变的,将其用作HashMap的键通常不是一个好主意,因为将值存储在它下面会导致修改后无法访问该值
我注意到您正在使用新字符串(“abc”);这意味着您知道字符串a=“abc”和字符串b=“abc”是相同的
因此,a==b返回true。a.equals(b)返回true
但是,同样的事情对StringBuffers不起作用,因为它的equals不考虑对象的值,只考虑其哈希代码
如果查看StringBuffer,您将看到它使用Object.equals,这是
public boolean equals(Object obj) {
return (this == obj);
}
而字符串的等号为:
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;
}
要理解这种行为,理解Hashmaps是如何工作的是很重要的。哈希映射使用键对象(本例中为字符串对象)返回的哈希代码值将它们存储到称为bucket的适当内存分区中。因此,在检索与之相关联的值时,hashmaps需要找到存储密钥的分区,然后针对该密钥返回值。Hashmaps根据密钥的哈希代码值标识隔室
现在,想象一下,如果我有两个具有相同哈希代码值的对象,会发生什么?在这种情况下,hashmap首先需要知道两个对象是否相同,因为如果它们相同,则表示映射中只有一个条目,与该键关联的现有值将被替换为新值。但是,仅仅拥有相同的散列码并不意味着两个键是相等的。因此,相等性是通过对键对象调用.equals()方法来确定的。如果.equals()返回true,则对象是相等的,在这种情况下,哈希映射需要更新现有条目的值。但是如果.equals()返回false怎么办?在这种情况下,两个对象是不同的,应该作为单独的条目存储。因此,它们并排存放在同一个隔间中。因此,在检索值时,使用输入键的哈希代码到达存储它的隔室,然后如果隔室包含多个对象,则检查输入键是否与隔室中的每个对象相等,如果匹配,则返回相关值
现在,让我们将上述理论应用到您的代码中。如果字符串对象的内容相等,则它们相等。根据规则,如果两个对象相等,它们应该返回相同的哈希代码。但请记住,匡威不是真的。如果两个对象返回相同的哈希代码,则不要求它们相等。这在一开始似乎令人困惑,但您可以在几次迭代中克服它。两个具有相同内容的字符串是相等的,即使它们是物理上不同的对象,也返回相同的哈希代码,因此,当在hashmap中用作键时,它们将始终映射到相同的条目。这就是行为
String类重写默认的equals()方法,该方法表示,如果两个对象的引用与依赖于相等内容的对象的引用相同,则两个对象是相等的。它可以这样做,因为字符串是不可变的。但是,StringBuffer不会这样做。它仍然依赖于引用相等。注意sb1.equals(sb2)
是错误的,因为它们是完全不同的东西。字符串就是字符串。字符串生成器在你将其转换为字符串之前不是字符串。你能解释一下为什么你认为它们应该是相同的吗?造成这种不一致的确切区别是什么。两者都是内部字符[]。不是吗?字符串是不可变的,这意味着它不会改变。StringBuilder是可变的,这意味着仅仅因为两个StringBuilder现在恰好包含相同的文本,并不意味着它们将来会包含相同的文本。。。不,不是这样,这些字符串是不同的对象,因为它们是用new
创建的,而不是interned。请自己尝试:String a=“abc”;字符串b=“abc”;System.out.println(a==b)代码>你应该自己试试。代码是strings1=新字符串(“abc”)代码>,而不是字符串a=“abc”代码>。注意new
的用法。哦,伙计,看看我贴的代码。我没有使用OP提供的。请阅读我在文章开头所说的内容。我现在明白了,尽管我不确定这如何适用于OPs问题,这归结为StringBuilder
没有覆盖equals
和hashCode
。无论如何,很抱歉误读了。
public boolean equals(Object obj) {
return (this == obj);
}
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;
}