如果我像这样String[] Distro = Distros.split(",");
那样直接初始化String数组,那么它将创建一个对象,因为变量Distro
持有该数组。
但是,如果我这样做,它还会创建一个对象吗?
String Distros = "CentOS,RHEL,Debian,Ubuntu";
for (String s : Distros.split(",")) {
System.out.println(s);
}
我的目标是减少对象创建,以最大程度地减少垃圾。
答案 0 :(得分:2)
您的推理“然后它将创建对象,因为变量Distro
包含数组”表示您将对象创建与变量分配相混淆。
对象是由表达式Distros.split(",")
创建的,而不是由后续赋值创建的。当您考虑到split
方法是一个普通的Java方法,该方法会创建并返回数组而无需任何有关调用者将如何处理结果的知识时,应该变得显而易见。
当该操作发生在对性能有严格要求的代码中时,您可以使用
int p = 0;
for(int e; (e = Distros.indexOf(',', p)) >= 0; p = e+1)
System.out.println(Distros.substring(p, e));
System.out.println(Distros.substring(p));
相反。值得指出的是,这可以节省数组的创建时间,但仍然可以执行子字符串的创建,这是它更昂贵的方面。在不知道您实际上将如何使用子字符串的情况下,无法说是否有其他方法可以节省子字符串的创建¹。
但是,此循环仍然比split
方法有优势。 split
方法创建所有子字符串,并返回一个数组,其中包含对它们的引用,并在整个循环中强制它们同时存在。上面的循环在需要时调用substring
,而在下一个循环时不保留引用。因此,不必强制字符串一直存在,并且垃圾回收器可以自由决定何时收集它们,具体取决于当前的内存利用率。
¹我假设打印只是一个例子。但是,要保留该示例,您可以替换
System.out.println(Distros.substring(p, e));
使用
System.out.append(Distros, p, e).println();
问题是,这只会隐藏子字符串的创建,至少在参考实现中会隐藏该子字符串的创建,最终将在后台执行子字符串的创建。
一种替代方法是
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(
new FileOutputStream(FileDescriptor.out)));
try {
int p = 0; for(int e; (e = Distros.indexOf(',', p)) >= 0; p = e+1) {
bw.write(Distros, p, e - p);
bw.write(System.lineSeparator());
}
bw.write(Distros, p, Distros.length() - p);
bw.write(System.lineSeparator());
bw.flush();
}
catch(IOException ex) {
ex.printStackTrace();
}
真正写字符串而无需创建子字符串。但这迫使我们处理通常PrintStream
隐藏的潜在异常。
答案 1 :(得分:1)
方法split(delimiter)
根据定界符从字符串中返回字符串数组,您在每个字符串中创建的字符串数组的内容以及每个字符串数组的范围都在此之后终止,因此GC可以释放它了>
String Distros = "CentOS,RHEL,Debian,Ubuntu";
for (String s : Distros.split(",")) {
System.out.println(s);
}
,等同于
String Distros = "CentOS,RHEL,Debian,Ubuntu";
System.out.println("start scope");
{
String[] splitArray = Distros.split(",");
for (String s : splitArray) {
System.out.println(s);
}
}
System.out.println("end scope");