我对两个代码感到困惑,为什么我要在这里给出的第二个代码比第一个更有效。
这两个代码都只是反转一个字符串,但是第一个代码比另一个代码慢,而且我不明白为什么。
第一个代码是:
String reverse1(String s) {
String answer = "";
for(int j = s.length() - 1; j >= 0; j--) {
answer += s.charAt(j);
}
return answer;
}
第二个代码是:
String reverse2(String s) {
char answer[] = new char[s.length()];
for(int j = s.length() - 1; j >= 0; j--) {
answer[s.length() - j - 1] = s.charAt(j);
}
return new String(answer);
}
我无法理解第二个代码比第一个代码更有效率,对此我将不胜感激。
答案 0 :(得分:3)
第一个代码声明
String answer;
字符串是不可变的。因此,每个追加操作都会重新分配整个字符串,将其复制,然后复制为新字符。
第二个代码声明
char answer[];
数组是可变的,因此每次迭代仅复制一个字符。最终的字符串仅创建一次,而不是在循环的每次迭代中创建一次。
答案 1 :(得分:1)
您的问题也许很难准确回答,部分原因是答案将取决于第一个版本的实际实现。反过来,这取决于您使用的Java版本以及编译器决定做什么。
假设编译器在编写时保持逐字记录第一个版本,那么是的,第一个版本可能效率更低,因为它将需要为反转过程中的每个步骤分配一个新的字符串。相反,第二个版本仅维护单个字符数组。
但是,如果编译器足够聪明,可以使用StringBuilder
,那么答案就会改变。考虑以下第一个版本:
String reverse1(String s) {
StringBuilder answer = new StringBuilder();
for (int j = s.length() - 1; j >= 0; j--)
answer.append(s.charAt(j));
return answer;
}
在幕后,StringBuilder
是使用字符数组实现的。因此,调用StringBuilder#append
有点类似于第二个版本,即它只是在缓冲区的末尾添加了新字符。
因此,如果您的第一个版本使用文字String
执行,那么它的效率要比第二个版本低,但是使用StringBuilder
可能与第二个版本相提并论。
答案 2 :(得分:0)
字符串是不可变的。每当您执行answer += s.charAt(j);
时,它都会创建一个新对象。尝试使用-XX:+PrintGCDetails
打印GC日志,看看延迟是否是由次要GC引起的。
答案 3 :(得分:0)
String
对象是不可变的,每次执行添加操作时,您都会创建另一个对象,分配空间等,因此当您需要连接多个字符串时,效率很低。
您的char数组方法非常适合您的特定需求,但是如果您需要更多通用字符串连接支持,则可以考虑使用StringBuilder
答案 4 :(得分:0)
在此代码中,您将在每次循环迭代中创建一个新的String对象,因为String是不可变的类
String reverse1(String s) {
String answer = "";
for (int j = s.length() - 1; j >= 0; j--)
answer += s.charAt(j);
return answer;
}
在此代码中,您已经为char数组分配了内存,您的代码将在最后一行仅创建单个String,因此效率更高
String reverse2(String s) {
char answer[] = new char[s.length()];
for (int j = s.length() - 1; j >= 0; j--)
answer[s.length() - j - 1] = s.charAt(j);
return new String(answer);
}
答案 5 :(得分:0)
为什么第二个代码比第一个更有效?
String是不可改变的,通过answer += s.charAt(j);
,您将在每个循环中创建String的新实例,这会使您的代码变慢。
为了提高性能和可读性,建议您在单线程上下文中使用StringBuilder
,而不是使用String(可能比固定大小的char数组慢一点,但具有更好的可读性):
String reverse1(String s) {
StringBuilder answer = new StringBuilder("");
for (int j = s.length() - 1; j >= 0; j--)
answer.append(s.charAt(j));
return answer.toString();
}
答案 6 :(得分:0)
JVM将字符串视为不可变的。因此,每次附加到现有字符串时,实际上就是在创建一个新字符串!这意味着必须为每次循环迭代在堆中创建一个新的字符串对象。创建对象并维持其生命周期有其开销。再加上被丢弃的字符串的垃圾回收(在上一个迭代中创建的字符串在下一个迭代中不会对其进行引用,因此,它是由JVM收集的。)
您应该考虑使用StringBuilder。我进行了一些测试,StringBuilder代码花费的时间并不比定长数组的时间小很多。
JVM处理字符串的方式有些细微差别。 JVM进行了诸如字符串实习之类的事情,因此不必为具有相同内容的多个字符串创建新对象。您可能需要调查一下。