Java,HashMaps和使用字符串作为键 - 字符串值是否存储两次?

时间:2010-03-13 09:45:47

标签: java string hashmap string-interning

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

HashMap<String, MyObject>

String键是MyObject中的字段,此字符串值是否存储两次?

所以当我添加条目时:

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

我是否在内存方面使用了两倍的字符串大小?或者Java在幕后做了哪些聪明的事情?

谢谢

3 个答案:

答案 0 :(得分:16)

除非您实际在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个支持char[]


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

此处,就像之前一样,s1 != s2。但是,这次使用了不同的构造函数,而不是char[]。为了确保不变性,toCharArray() 必须返回其内部数组的防御副本(这样对返回数组的任何更改都不会改变String值)。

  

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

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


回到你的问题,只要你没有在getName()中创建一个新的String值(即如果你只是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

答案 1 :(得分:4)

Java使用引用,因此它只是指向它存储两次的字符串的指针。所以你不必担心你的字符串是否很大,它仍然会使用相同的内存量。

答案 2 :(得分:1)

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