在通过“加法”的循环逐个字符地构建Java String char时,可以发现执行此操作的时间复杂度很差:它是二次的 O(n^2)
时间。但是,我想知道通过循环“逐个字符地添加”一个字符串的空间复杂性是否也很差。
这是一个程序的最小示例,该程序通过具有秒表计时功能的逐个字符添加来执行构建String的操作。我在下面显示的结果清楚地显示出二次曲线:
/**Create a String of n 'A's char-by-char.
* @param n Number of 'A's to be appended
* @return The finished string
*/
public static String appendString(int n) {
String str = "";
// Timed run starts here
long t = System.currentTimeMillis();
// String concatenation occurs here
for (int k = 0; k < n; k++)
str += "A";
t = System.currentTimeMillis() - t;
// Timed run ends
System.out.printf("%d\t%d\n", n, t);
return str;
}
尽管我可以获得程序的时间复杂度,但我想知道构建逐个字符的字符串的空间复杂度是什么?
首选解决内存管理的详细答案。 (我怀疑这也是平方的,因为字符串是不可变的,并且每次您必须为不断增长的字符串分配新的内存时就可以使用
注意:这不是String concatenation complexity in C++ and Java的重复,因为它不能解决空间复杂性。我特别要求详细的空间复杂度分析。
答案 0 :(得分:2)
您的代码效率很低,因为,将创建一个隐式StringBuilder
来在循环中添加单个字符。这个
for (int k = 0; k < n; k++)
str += "A";
等价于
for (int k = 0; k < n; k++)
str = new StringBuilder(str).append("A").toString();
您可以通过使用单个StringBuilder
来极大地提高空间(和时间)性能(并且可以明确调整其大小)。喜欢,
StringBuilder sb = new StringBuilder(n);
for (int k = 0; k < n; k++)
sb.append("A");
String str = sb.toString();
或,在Java 8+中,您可以使用生成器并将其限制为n
次并进行收集。喜欢,
return Stream.generate(() -> "A").limit(n).collect(Collectors.joining());
答案 1 :(得分:1)
它使用二次空间。或者,而是分配了二次空间,因为循环的每次迭代(至少在JIT不会巧妙执行的代码中)都会分配一个新的char数组:
new char[1]
new char[2]
new char[3]
// Etc.
之所以具有二次时间性能,是因为将字符串复制到了这些越来越大的数组中。