【发布时间】:2015-09-14 09:37:36
【问题描述】:
StringBuffer 可以作为 HashMap 中的键吗?
如果是这样,使用 String 和 StringBuffer 作为 key 有什么区别?
【问题讨论】:
-
您指的是什么语言或库?你应该用它来标记你的问题。
-
@jB 语言是java
StringBuffer 可以作为 HashMap 中的键吗?
如果是这样,使用 String 和 StringBuffer 作为 key 有什么区别?
【问题讨论】:
StringBuffer 可以作为 HashMap 中的键吗?
不,因为StringBuffer 既不覆盖equals 也不覆盖hashCode,所以它不适合作为HashMap 键(回想一下HashMap 依赖于这两种方法来判断给定的键是否存在于地图)。
除此之外,StringBuffers 是可变的,您通常希望 Map 键是不可变的。来自Map:
注意:如果将可变对象用作映射键,则必须非常小心。如果对象的值以影响
equals比较的方式更改,而对象是映射中的键,则不指定映射的行为。此禁令的一个特殊情况是不允许映射包含自己作为键。虽然允许映射将自身作为值包含在内,但建议格外小心:equals和hashCode方法在此类映射上不再明确定义。
【讨论】:
不,你不能,除非你想区分单独的缓冲区而不是它们的内容。 StringBuffer 类没有实现 equals 或 hashCode,这意味着它从 Object 继承了这些方法。这些方法的Object 实现只区分对象实例,而不区分它们的内容。
换句话说,如果您有两个具有相同内容的StringBuffer 实例,则它们不会被视为相等。更奇怪的是,如果你用不同的值重新插入同一个缓冲区,它会被认为等于前一个。
一般来说,您应该小心使用可变值作为键。突变不会改变Map 中的位置,因为Map 实例不会收到更改通知。在这种情况下,由于equals无论如何都没有实现,所以不会出现这个问题。
【讨论】:
java 中的所有类都旨在用作哈希键,因为它们都继承了超方法hashCode。尽管在某些情况下,尽管它可能编译得很好,但会很奇怪,例如 Connection 或 Streams... 或 StringBuffer。这就是为什么:
String 和StringBuffer 之间的主要区别在于,String 在设计上是不可变,它包含hashCode 的正确实现。相反,StringBuffers 可能会更改,因此,此类没有正确实现 hashCode:它不会覆盖从 Object 继承的默认实现。现在你可以看到后果了:StringBuffer 不能包含高质量的哈希,也不能与其内容保持一致,从而破坏了哈希算法的结果。
【讨论】:
是的,任何对象都可以用作HashMap 中的键,尽管这可能不是一个好主意。
类 HashMap
Type Parameters: K - the type of keys maintained by this map V - the type of mapped values
当您将键值对放入映射时,哈希映射将查看 密钥的哈希码,并将该对存储在其中的桶中 标识符是密钥的哈希码。 (...) 看上面 机制,您还可以看到需要什么要求
hashCode()和equals()方法的键(...)
但是请注意,StringBuffer 不会覆盖所需的方法,因此您的“键”将是对象的内存地址。来自the hashcode() docs:
(这通常通过转换内部地址来实现 对象转换成整数,但这种实现技术不是 JavaTM 编程语言所要求的。)
意味着它用作密钥将与 String 的非常不同:
Map<String, String> hashA = new HashMap<>();
a.put('a', 'a');
System.out.println(hashA.get('a')); //prints 'a'
Map<StringBuffer, String> hashB = new HashMap<>();
StringBuffer buffer = new StringBuffer('a');
hashB.put(buffer, 'a');
System.out.println(hashB.get(new StringBuffer('a'))); //prints null
System.out.println(hashB.get(buffer)); //prints 'a'
【讨论】: