4

如果我有一个看起来像这样的 HashMap:

HashMap<String, MyObject>

其中String键是 中的一个字段MyObject,这个字符串值是否被存储两次?

所以当我添加条目时:

_myMap.put(myObj.getName(), myObj);

我在内存方面使用双倍的字符串大小吗?还是 Java 在幕后做了一些聪明的事情?

谢谢

4

3 回答 3

16

除非您实际上是在 中创建一个新的 String 值getName(),否则您不会重复使用内存。

这里有几个例子来说明问题:

 String s1 = "Some really long string!";
 String s2 = s1;
 assert s1.equals(s2);

在这里,s1 == s2; 他们指的是同一个String例子。您的内存使用量是 2 个引用变量(没什么大不了的)、1 个String实例和1 个支持char[](占用内存的部分)。


 String s1 = "Some really long string!";
 String s2 = new String(s1);
 assert s1.equals(s2);

在这里,s1 != s2; 它们指的是不同的String实例。但是,由于字符串是不可变的,构造函数知道它们可以共享相同的字符数组。您的内存使用量是 2 个引用变量、2 个String实例(仍然没什么大不了的,因为...)和 1 个 backing char[]


 String s1 = "Some really long string!";
 String s2 = new String(s1.toCharArray());
 assert s1.equals(s2);

在这里,和以前一样,s1 != s2。使用了不同的构造函数,但是,这一次char[]取而代之的是 a。为了确保不变性,toCharArray() 必须返回其内部数组的防御性副本(这样对返回数组的任何更改都不会改变字符串值)。

[ toCharArray()返回]一个新分配的字符数组,其长度为该字符串的长度,其内容被初始化为包含该字符串表示的字符序列。

更糟糕的是,构造函数还必须防御性地将给定数组复制到其内部支持数组,以确保不变性。这意味着多达 3 个字符数组的副本可能同时存在于内存中!其中 1 个最终将被垃圾收集,因此您的内存使用量是 2 个引用变量、2 个String实例和2 个支持char[]现在您的内存使用量翻了一番!


因此,回到您的问题,只要您没有在其中创建新的 String 值getName()(即,如果您只是简单地return this.name;),那么您就可以了。但是,如果您甚至进行简单的连接(例如return this.firstName + this.lastName;),那么您的内存使用量就会翻倍!

以下代码说明了我的观点:

public class StringTest {
    final String name;
    StringTest(String name) {
        this.name = name;
    }
    String getName() {
        return this.name;      // this one is fine!
    //  return this.name + ""; // this one causes OutOfMemoryError!
    }
    public static void main(String args[]) {
        int N = 10000000;
        String longString = new String(new char[N]);
        StringTest test = new StringTest(longString);
        String[] arr = new String[N];
        for (int i = 0; i < N; i++) {
            arr[i] = test.getName();
        }
    }
}

您应该首先验证上述代码是否运行 ( java -Xmx128m StringTest) 没有抛出任何异常。然后,修改getName()return this.name + "";并再次运行它。这次你会得到一个OutOfMemoryError.

于 2010-03-13T11:13:57.893 回答
4

Java 使用引用,因此它只是一个指向它存储两次的字符串的指针。因此,如果您的字符串很大,您不必担心,它仍然会使用相同数量的内存。

于 2010-03-13T09:52:30.227 回答
1

字符串是不可变的,但引用传递仍然适用。所以它不会占用两倍的内存。

于 2010-03-13T09:53:06.573 回答