Java、HashMap 和使用字符串作为键 - 字符串值是否会存储两次?
如果我有一个如下所示的 HashMap:
HashMap
其中 String
键是 MyObject
中的字段,则此字符串是否值被存储两次?
因此,当我添加条目时:
_myMap.put(myObj.getName(), myObj);
就内存而言,我是否使用了字符串大小的两倍?还是 Java 在幕后做了一些聪明的事情?
谢谢
If I have a HashMap that looks like this:
HashMap<String, MyObject>
where the String
key is a field in MyObject
, does this string value get stored twice?
So when I add entries:
_myMap.put(myObj.getName(), myObj);
Am I using double the String size in terms of memory? Or does Java do something clever behind the scenes?
Thanks
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
除非您实际上在
getName()
中创建新的字符串值,否则您不会重复内存使用情况。这里有几个例子来澄清问题:
这里,
s1 == s2
;它们引用同一个String
实例。你的内存使用量是2个引用变量(没什么大不了的),1个String
实例,和1个支持char[]
(占用的部分记忆)。这里,
s1 != s2
;它们引用不同的String
实例。但是,由于字符串是不可变的,构造函数知道它们可以共享相同的字符数组。您的内存使用量是 2 个引用变量、2 个String
实例(仍然没什么大不了的,因为......)和 1 个支持char[]
。在这里,就像之前一样,
s1 != s2
。不过,这次使用了不同的构造函数,它采用char[]
来代替。为了确保不变性,toCharArray()
必须返回其内部数组的防御性副本(这样对返回数组的任何更改都不会改变 String 值)。更糟糕的是,构造函数还必须防御性地将给定数组复制到其内部支持数组,再次确保不变性。这意味着内存中可能同时存在多达 3 个字符数组的副本!其中 1 个最终将被垃圾收集,因此您的内存使用量是 2 个引用变量、2 个 String 实例和 2 个支持 char[] ! 现在您的内存使用量增加了一倍!
因此,回到您的问题,只要您没有在 getName() 中创建新的字符串值(即,如果您只是简单地返回 this.name; ),那你就没事了。但是,如果您执行的是简单的串联(例如
return this.firstName + this.lastName;
),那么您的内存使用量将会增加一倍!以下代码说明了我的想法要点:
您应该首先验证上述代码是否运行(
java -Xmx128m StringTest
)而不会引发任何异常。然后,修改getName()
以return this.name + "";
并再次运行。这次你会得到一个OutOfMemoryError
。Unless you're actually creating a new String value in
getName()
, you're not duplicating your memory usage.Here are a few examples to clarify things:
Here,
s1 == s2
; they refer to the sameString
instance. Your memory usage is 2 reference variables (no big deal), 1String
instance, and 1 backingchar[]
(the part that takes up memory).Here,
s1 != s2
; they refer to differentString
instances. However, since strings are immutable, the constructor knows that they can share the same character array. Your memory usage is 2 reference variables, 2String
instances (still no big deal, because...), and 1 backingchar[]
.Here, just like before,
s1 != s2
. A different constructor is used, this time, however, that takes achar[]
instead. To ensure immutability,toCharArray()
must return a defensive copy of its internal array (that way any changes to the returned array would not mutate the String value).To make matters worse, the constructor must also defensively copy the given array to its internal backing array, again to ensure immutability. This means that as many as 3 copies of the character array may live in the memory at the same time! 1 of those will be garbage-collected eventually, so your memory usage is 2 reference variables, 2
String
instances, and 2 backingchar[]
! NOW your memory usage is doubled!So going back to your question, as long as you're not creating a new String value in
getName()
(i.e. if you just simplyreturn this.name;
), then you're fine. If you are doing even a simple concatenation, however (e.g.return this.firstName + this.lastName;
), then you will double your memory usage!The following code illustrates my point:
You should first verify that the above code runs (
java -Xmx128m StringTest
) without throwing any exception. Then, modifygetName()
toreturn this.name + "";
and run it again. This time you will get anOutOfMemoryError
.Java 使用引用,因此它只是一个指向它存储两次的字符串的指针。因此,如果您的字符串很大,您不必担心,它仍然会使用相同的内存量。
Java uses the reference, so it is just a pointer to the string that it stores twice. So you don't have to worry if your string is huge, it will still be the same amount of memory that is used.
字符串是不可变的,但引用传递仍然适用。所以它不会占用两倍的内存。
String are immutable, but pass-by-reference still apply. So it won't take twice as much memory.