28

这是一个面试问题。我被要求实现StringBuffer附加功能。面试后看到了代码。但我无法理解如何通过创建单个对象来完成操作。

我是这样想的。

String s = "orange";
s.append("apple");

这里创建了两个对象。

StringBuilder s = new StringBuilder("Orange");
s.append("apple");

现在这里只创建了一个对象。

Java是如何做这个操作的?

4

10 回答 10

54

首先你的问题有问题:

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,因为它是非线程安全的实现。该代码很有趣且易于阅读。我添加了内联注释。

作为内部结构,有一个字符数组,而不是字符串。它最初是用长度 16 建造的,每次超过容量时都会增加。如果要追加的字符串适合 char 数组,则无需创建新对象。

StringBuilderextends 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) 复制指定的数组,截断或填充空字符(如果需要),使副本具有指定的长度。

在我们的例子中,填充,因为我们正在扩展长度。

于 2011-11-04T15:11:44.267 回答
9

来源是你的朋友,卢克!

这是AbstractStringBuilder的来源

于 2011-11-04T15:07:45.540 回答
4

String是不可变的。附加一个字符串只能生成一个新字符串。

StringBuilder是可变的。追加到 aStringBuilder是就地操作,就像添加到 ArrayList 一样。

于 2011-11-04T15:08:01.457 回答
4

这不编译。

String S= "orange";
S.append("apple");

如果你这样做

final String S= "orange";
final S2 = S + "apple";

这不会创建任何对象,因为它在编译时针对两个字符串文字进行了优化。

StringBuilder s = new StringBuilder("Orange");
s.append("apple");

这将创建两个对象StringBuilderchar[]对其进行包装。如果你使用

String s2 = s.toString();

这将创建另外两个对象。

如果你这样做

String S= "orange";
S2 = S + "apple";

这与

String S2 = new StringBuilder("orange").append("apple").toString();

它创建了 2 + 2 = 4 个对象。

于 2011-11-04T15:13:27.237 回答
2

StringBuffer 与 StringBuilder 一样,分配一个 char 数组,将附加的字符串复制到该数组中。它仅在字符数超过数组大小时创建新对象,在这种情况下,它会重新分配并复制数组。

于 2011-11-04T15:10:43.153 回答
1

StringBuilderchar在 a 中保存一个 s的缓冲区,并在调用时char[]将它们转换为 a 。StringtoString

于 2011-11-04T15:11:32.820 回答
1
String s = "orange";
s.append("apple");

这是不正确的,因为附加方法在 String 中不可用:

于 2012-02-28T20:30:10.327 回答
1

tl;dr:简单来说,使用字符的每个字符串连接表达式+都会导致一个新String对象,其中初始字符串的内容被复制到新对象。StringBuffer拥有一个仅在需要时扩展的内部结构,即附加字符。

嘿,但是很多人使用+字符串连接!

好吧,我们/他们不应该。

在内存使用方面,您正在使用一个数组StringBuffer来保存字符 - 调整大小,事实,但很少如果应用于调整大小的算法是有效的,并且只调用一个String创建一次的对象,toString()String在每个+连接上创建一个新对象。

就时间复杂度而言,字符只复制一次_chars到新字符串(O(n)时间复杂度),这通常比使用运算符的字符串连接要好+,在该运算符上,每个操作都会将字符的新副本复制到新对象,导致O(1 + 2 + .... + n) = O(n^2)操作。

我应该自己实施一个吗?

就练习而言,这对您有好处,但是现代语言提供了本机StringBuffer实现以在生产代码中使用它。

四个简单的步骤:

  1. 创建一个在内部(私下)保存固定初始大小的字符MyCustomStringBuilder数组(让我们命名它)的类。_chars该数组将保存字符串字符。
  2. Add an expanding method that will increase the size of _chars once the holding string character length exceeds its length. (What you are practically doing, is implementing a simple version of an ArrayList internally).
  3. When using the stringBufferInstance.append(String s) method, add characters to _chars, increasing its size if needed.
  4. In your toString() method implementation, you can simply create a string using the array:

    public String toString() {
        return new String(_chars);
    }
    
于 2016-07-02T14:50:12.543 回答
0

正如其他人所描述的,StringBuffer 它是可变的,它是通过使用char数组来实现的。中的操作StringBuffer是就地操作。

更多信息可从以下链接获得 http://www.concentric.net/~ttwang/tech/jfastbuf.htm

char它显示了使用数组的简单 StringBuffer 实现。

于 2011-11-04T15:14:02.850 回答
-1
****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****
于 2012-02-28T20:38:35.847 回答