StringBuffer 如何在不创建两个对象的情况下实现 append 功能?
Posted
技术标签:
【中文标题】StringBuffer 如何在不创建两个对象的情况下实现 append 功能?【英文标题】:How is StringBuffer implementing append function without creating two objects? 【发布时间】:2011-12-22 02:50:32 【问题描述】:这是一个面试问题。我被要求实现StringBuffer
附加功能。面试后看到了代码。但我无法理解如何通过创建单个对象来完成操作。
我是这样想的。
String s = "orange";
s.append("apple");
这里创建了两个对象。
但是
StringBuilder s = new StringBuilder("Orange");
s.append("apple");
现在这里只创建了一个对象。
Java 是如何进行这个操作的?
【问题讨论】:
问题中有一些不正确的假设。new StringBuilder()
和 new String()
创建两个对象。
是我的问题吗? ;)
【参考方案1】:
首先你的问题有问题:
String s = "orange";
s.append("apple");
这里创建了两个对象
正确,创建了两个对象,字符串“orange”和字符串“apple”,在 StringBuffer/StringBuilder 中,如果我们不溢出缓冲区,则不会创建任何对象。所以这些代码行创建了 2 或 3 个对象。
StringBuilder s = new StringBuilder("Orange");
s.append("apple");
现在这里只创建了一个对象
我不知道你从哪里得到的,在这里你创建一个 StringBuilder 对象,一个“橙色”字符串,一个“苹果”字符串,总共 3 个对象,或者如果我们溢出 StringBuilder 缓冲区,则为 4 个。 (我把数组的创建算作对象的创建)。
我将您的问题读为,StringBuilder 如何在不创建新对象的情况下进行追加(当缓冲区未溢出时)?
你应该看看StringBuilder
,因为它是非线程安全的实现。该代码很有趣且易于阅读。我已经添加了内联 cmets。
作为内部结构,有一个字符数组,而不是字符串。它最初建造长度为 16,每次超过容量时都会增加。如果要追加的字符串适合 char 数组,则无需创建新对象。
StringBuilder
扩展AbstractStringBuilder
,您将在其中找到以下代码:
/**
* The value is used for character storage.
*/
char value[];
由于并非所有数组都会在给定时间使用,另一个重要的变量是长度:
/**
* The count is the number of characters used.
*/
int count;
append的重载有很多,但最有趣的是以下:
public AbstractStringBuilder append(String str)
if (str == null) str = "null"; //will literally append "null" in case of null
int len = str.length(); //get the string length
if (len == 0) return this; //if it's zero, I'm done
int newCount = count + len; //tentative new length
if (newCount > value.length) //would the new length fit?
expandCapacity(newCount); //oops, no, resize my array
str.getChars(0, len, value, count); //now it will fit, copy the chars
count = newCount; //update the count
return this; //return a reference to myself to allow chaining
String.getChars(int srcBegin, int srcEnd, char[] dst, int dstBegin) 将此字符串中的字符复制到目标字符数组中。
所以,append 方法很简单,剩下的唯一魔法就是expandCapacity
,这里是:
void expandCapacity(int minimumCapacity)
//get the current length add one and double it
int newCapacity = (value.length + 1) * 2;
if (newCapacity < 0) //if we had an integer overflow
newCapacity = Integer.MAX_VALUE; //just use the max positive integer
else if (minimumCapacity > newCapacity) //is it enough?
//if doubling wasn't enough, use the actual length computed
newCapacity = minimumCapacity;
//copy the old value in the new array
value = Arrays.copyOf(value, newCapacity);
Arrays.copyOf(char[] original, int newLength) 复制指定的数组,截断或填充空字符(如果需要),使副本具有指定的长度。
在我们的例子中,填充,因为我们正在扩展长度。
【讨论】:
你能解释一下为什么在expandCapacity中,首先value.length加1,然后再乘2吗?是否需要增加 1 来说明空字符【参考方案2】:source 是你的朋友,卢克!
这里是AbstractStringBuilder的来源
【讨论】:
AbstractStringBuilder 是 StringBuilder 的父类【参考方案3】:String
是不可变的。追加字符串只能生成新字符串。
StringBuilder
是可变的。追加到 StringBuilder
是一种就地操作,就像添加到 ArrayList。
【讨论】:
嗨 Slaks,我知道。我想知道字符串生成器是如何进行该操作的。【参考方案4】:这不会编译。
String S= "orange";
S.append("apple");
如果你这样做
final String S= "orange";
final S2 = S + "apple";
这不会创建任何对象,因为它在编译时被优化为两个字符串文字。
StringBuilder s = new StringBuilder("Orange");
s.append("apple");
这将创建两个对象StringBuilder
和它包装的char[]
。如果你使用
String s2 = s.toString();
这又创建了两个对象。
如果你这样做
String S= "orange";
S2 = S + "apple";
这是一样的
String S2 = new StringBuilder("orange").append("apple").toString();
创建 2 + 2 = 4 个对象。
【讨论】:
【参考方案5】:StringBuffer 与 StringBuilder 一样,分配一个 char 数组,将附加的字符串复制到该数组中。它仅在字符数超过数组大小时创建新对象,在这种情况下它会重新分配并复制数组。
【讨论】:
【参考方案6】:StringBuilder
在char[]
中保存char
s 的缓冲区,并在调用toString
时将它们转换为String
。
【讨论】:
【参考方案7】:String s = "orange";
s.append("apple");
这是不正确的,因为在 String 中没有 append 方法:
【讨论】:
【参考方案8】:tl;dr: 简单来说,使用+
字符的每个字符串连接表达式 都会导致一个新的String
对象,其中包含初始字符串的内容被复制到新的。 StringBuffer
拥有一个仅在需要时扩展的内部结构,即附加字符。
嘿,但是很多人使用+
字符串连接!
好吧,我们/他们不应该。
在内存使用方面,您在StringBuffer
中使用了一个数组来保存字符 - 调整大小,事实上,但如果用于调整大小的算法是有效的,那么很少,并且只有一个String
对象是在调用 toString()
时创建,比在每个 +
连接上创建一个新的 String
对象要好得多。
就时间复杂度而言,字符仅从_chars
复制到新字符串一次(O(n)
时间复杂度),这通常比使用+
运算符的字符串连接要好,每个操作都在导致将字符的新副本复制到新对象,从而导致O(1 + 2 + .... + n) = O(n^2)
操作。
我应该自己实现一个吗?
在练习方面对您有好处,但现代语言提供原生 StringBuffer
实现以在生产代码中使用它。
只需四个简单的步骤:
-
创建一个
MyCustomStringBuilder
类,该类在内部(私下)保存一个固定初始大小的字符数组(我们将其命名为_chars
)。这个数组将持有
字符串字符。
添加将_chars
的大小增加一次的扩展方法
保持字符串字符长度超过其长度。 (你什么
实际上是在实现一个简单版本的
ArrayList
内部)。
使用stringBufferInstance.append(String s)
方法时,添加
字符到_chars
,如果需要增加它的大小。
在您的toString()
方法实现中,您可以简单地创建一个string using the array:
public String toString()
return new String(_chars);
【讨论】:
【参考方案9】:正如其他人所描述的,StringBuffer
是可变的,它是通过使用 char
数组来实现的。 StringBuffer
中的操作是就地操作。
更多信息可从以下链接获得 http://www.concentric.net/~ttwang/tech/jfastbuf.htm
它展示了使用 char
数组的简单 StringBuffer 实现。
【讨论】:
【参考方案10】:****String s1="Azad"; ----One object will create in String cons. pool
System.out.println(s1);--output--Azad
s1=s1.concat("Raja"); Two object will create 1-Raja,2-AzadRaja and address of AzadRaja Store in reference s1 and cancel ref.of Azad object
System.out.println(s1); --output AzadRaja****
【讨论】:
您的答案很难阅读。它是否添加了任何尚未包含在已接受答案中的有用信息?以上是关于StringBuffer 如何在不创建两个对象的情况下实现 append 功能?的主要内容,如果未能解决你的问题,请参考以下文章
Java入门系列之StringBuilderStringBuffer
java中StringBuffer与String创建对象问题???
String StringBuffer StringBuilder