我注意到Java String会在其中重用char数组,以避免在诸如subString()之类的方法中为新的String实例创建新的char数组。为此,String中有几个取消发布的构造函数,接受一个char数组和两个int作为范围来构造一个String实例。
但直到今天我发现split还将重用原始String实例的char arr。现在我从一个文件中读取一个loooooong行,用“,”拆分它,并为实际用法剪切一个极限列。因为线的每个部分都秘密地拿着looooong char数组的参考,我很快就得到了一个OOO。
这是示例代码:
ArrayList<String> test = new ArrayList<String>(3000000);
BufferedReader origReader = new BufferedReader(new FileReader(new File(
"G:\\filewithlongline.txt")));
String line = origReader.readLine();
int i = 0;
while ((line = origReader.readLine()) != null) {
String name = line.split(',')[0];
test.add(name);
i++;
if (i % 100000 == 0) {
System.out.println(name);
}
}
System.out.println(test.size());
JDK中是否有任何标准方法可以确保吐出的每个String实例都是“真正的深拷贝”而不是“浅拷贝”?
现在我正在使用一种非常难看的解决方法来强制创建一个新的String实例:
ArrayList<String> test = new ArrayList<String>(3000000);
BufferedReader origReader = new BufferedReader(new FileReader(new File(
"G:\\filewithlongline.txt")));
String line = origReader.readLine();
int i = 0;
while ((line = origReader.readLine()) != null) {
String name = line.split(',')[0]+" ".trim(); // force creating a String instance
test.add(name);
i++;
if (i % 100000 == 0) {
System.out.println(name);
}
}
System.out.println(test.size());
答案 0 :(得分:3)
最简单的方法是直接创建一个新的String。这是一个很好的主意的罕见案例之一。
String name = new String(line.split(",")[0]); // note the use of ","
另一种方法是自己解析文件。
do {
StringBuilder name = new StringBuilder();
int ch;
while((ch = origReader.read()) >= 0 && ch != ',' && ch >= ' ') {
name.append((char) ch);
}
test.add(name.toString());
} while(origReader.readLine() != null);
答案 1 :(得分:2)
String
有copy constructor可用于此目的。
final String name = new String(line.substring(0, line.indexOf(',')));
...或者,正如彼得所建议的那样,只需阅读,
。
final StringBuilder buf = new StringBuilder();
do {
int ch;
while ((ch = origReader.read()) >= 0 && ch != ',') {
buf.append((char) ch);
}
test.add(buf.toString());
buf.setLength(0);
} while (origReader.readLine() != null);