Java: 使用 StringBuilder 在开头插入数据

119

我只能用字符串来做这个,例如:

String str = "";
for (int i = 0; i < 100; i++) {
    str = i + str;
}

有没有办法用StringBuilder实现这个?谢谢。
9个回答

229
StringBuilder sb = new StringBuilder();
for(int i=0;i<100;i++){
    sb.insert(0, Integer.toString(i));
}

警告:这样做破坏了StringBuilder的设计思路,但可以实现你所要求的功能。


更好的技术(虽然仍不理想):

  1. 反转你想插入的每个字符串。
  2. 将每个字符串追加到StringBuilder中。
  3. 完成后反转整个StringBuilder

这将把一个O(n²)的解决方案变成O(n)。


2
由于这会使AbstractStringBuilder移动插入索引之后的所有内容以为插入内容腾出空间。然而,这只是一种实现细节,而不是原则性问题。 - entonio
1
@entonio:确实,但这是一个非常关键的细节。 :) - user541686
1
我明白了,看起来我不应该使用 StringBuilder,非常感谢。 - user685275
@user685275:是的,如果你需要反向插入,那么你确实需要能够在字符串开头插入的东西。我认为最简单的解决方案是上面的技巧(两次反转),尽管你可能可以用字符数组制作一个更好的类(可能要研究一下“deque”)。 - user541686
1
@rogerdpack:我会说这是机制,而不是目的。目的是为了比字符串操作渐进快,如果使用不当,它就做不到。 - user541686
显示剩余10条评论

38

您可以使用strbuilder.insert(0,i);


3
这条为什么有这么多赞!类没有被正确定义——只有方法调用的签名! - JGFMK
@JGFMK 我猜strbuilder是一个变量,而不是类名(确实,StringBuilder#insert不是一个静态方法,因此你不能像StringBuilder.insert(0,i)这样调用它)。 - logi-kal

14

也许我理解有误,但你想要得到一个看起来像这样的字符串,"999897969594...543210",对吗?

StringBuilder sb = new StringBuilder();
for(int i=99;i>=0;i--){
    sb.append(String.valueOf(i));
}

这篇帖子通过巧妙地操作循环提供了解决方案,但奇怪的是它没有得到太多的投票。 - nom-mon-ir
2
@nom-mon-ir 他只是将字符串反转了。这并没有回答如何在左侧添加的问题。 - Raymond Chenon
达到了预期的效果。 - Speck
我认为有些情况下,你可以使用这种方法来代替试图通过在开头插入的方式来利用StringBuilder的真正潜力。无论如何,有些情况下你无法反转循环,因此你还需要其他答案。 - PhoneixS

8
作为替代方案,您可以使用类似堆栈的LIFO结构来存储所有字符串,完成后将它们全部取出并放入StringBuilder中。这自然地倒转了放置其中的项目(字符串)的顺序。
Stack<String> textStack = new Stack<String>();
// push the strings to the stack
while(!isReadingTextDone()) {
    String text = readText();
    textStack.push(text);
}
// pop the strings and add to the text builder
String builder = new StringBuilder(); 
while (!textStack.empty()) {
      builder.append(textStack.pop());
}
// get the final string
String finalText =  builder.toString();

4
应该使用ArrayDeque而不是Stack。"更完整和一致的后进先出(LIFO)堆栈操作集由{@link Deque}接口及其实现提供,应优先使用该类而不是此类。" - Luna

4

这个帖子很老,但你也可以考虑使用递归解决方案,并将StringBuilder传递给fill方法。这样可以避免任何反向处理等问题。只需要使用递归来设计迭代,并仔细选择退出条件。

public class Test {

    public static void main(String[] args) {
        StringBuilder sb = new StringBuilder();
        doRecursive(sb, 100, 0);
        System.out.println(sb.toString());
    }

    public static void doRecursive(StringBuilder sb, int limit, int index) {
        if (index < limit) {
            doRecursive(sb, limit, index + 1);
            sb.append(Integer.toString(index));
        }
    }
}

3
您可以使用insert方法并设置偏移量。 当偏移量设置为'0'时,表示您正在将内容添加到StringBuilder的开头。
StringBuilder sb = new StringBuilder();
for(int i=0;i<100;i++){
    sb.insert(0,i);
}

注意:由于插入方法接受所有类型的原始数据,因此您可以将其用于int、long、char[]等类型。


2
我在偶然发现这篇文章时也有过类似的需求。我想要快速构建一个字符串,可以从两端增加新字母,即任意添加到前面和后面。虽然这是一篇旧帖子,但它激发了我尝试几种创建字符串的方法,并且我想与大家分享我的发现。在此过程中,我还使用了一些Java 8的结构,在第4和第5种情况下可能已经优化了速度。

https://gist.github.com/SidWagz/e41e836dec65ff24f78afdf8669e6420

上面的要点提供了详细的代码,任何人都可以运行。 我在这里使用了几种增加字符串的方法:1)将其附加到 StringBuilder 中,2)像 @Mehrdad 所示一样在 StringBuilder 的前面插入,3)部分地从 StringBuilder 的前面和后面插入,4)使用列表从末尾附加,5)使用双端队列从前面附加。
// Case 2    
StringBuilder build3 = new StringBuilder();
IntStream.range(0, MAX_STR)
                    .sequential()
                    .forEach(i -> {
                        if (i%2 == 0) build3.append(Integer.toString(i)); else build3.insert(0, Integer.toString(i));
                    });
String build3Out = build3.toString();


//Case 5
Deque<String> deque = new ArrayDeque<>();
IntStream.range(0, MAX_STR)
                .sequential()
                .forEach(i -> {
                    if (i%2 == 0) deque.addLast(Integer.toString(i)); else deque.addFirst(Integer.toString(i));
                });

String dequeOut = deque.stream().collect(Collectors.joining(""));

我将专注于前追加的情况,即第2种和第5种情况。StringBuilder的实现内部决定了内部缓冲区的增长方式,在前追加时,除了在移动所有缓冲区左到右的情况下限制速度之外,还会影响时间。虽然直接插入到StringBuilder前面的时间会增加到非常高的值,如@Mehrdad所示,但如果只需要长度小于90k个字符的字符串(仍然很多),则前插入将在与在末尾追加相同长度的字符串所需时间相同的时间内构建一个字符串。我的意思是,时间惩罚确实存在并且很大,但只有当你必须构建非常大的字符串时才会发生。可以使用deque并在末尾连接字符串,就像我的例子中所示。但是,StringBuilder更直观易读易编码,对于较小的字符串来说,惩罚并不重要。

实际上,第2种情况的性能比第1种情况要快得多,这一点我似乎无法理解。我假设在前追加和后追加的情况下,StringBuilder内部缓冲区的增长是相同的。我甚至将最小堆设置为非常大的数量,以避免堆增长延迟,如果这可能会起到作用的话。也许有更好理解的人可以在下面发表评论。


1
Difference Between String, StringBuilder And StringBuffer Classes
String
String is immutable ( once created can not be changed )object. The object created as a
String is stored in the Constant String Pool.
Every immutable object in Java is thread-safe, which implies String is also thread-safe. String
can not be used by two threads simultaneously.
String once assigned can not be changed.
StringBuffer
StringBuffer is mutable means one can change the value of the object. The object created
through StringBuffer is stored in the heap. StringBuffer has the same methods as the
StringBuilder , but each method in StringBuffer is synchronized that is StringBuffer is thread
safe .
Due to this, it does not allow two threads to simultaneously access the same method. Each
method can be accessed by one thread at a time.
But being thread-safe has disadvantages too as the performance of the StringBuffer hits due
to thread-safe property. Thus StringBuilder is faster than the StringBuffer when calling the
same methods of each class.
String Buffer can be converted to the string by using
toString() method.

    StringBuffer demo1 = new StringBuffer("Hello") ;

// The above object stored in heap and its value can be changed.
/
// Above statement is right as it modifies the value which is allowed in the StringBuffer
StringBuilder
StringBuilder is the same as the StringBuffer, that is it stores the object in heap and it can also
be modified. The main difference between the StringBuffer and StringBuilder is
that StringBuilder is also not thread-safe.
StringBuilder is fast as it is not thread-safe.
/
// The above object is stored in the heap and its value can be modified
/
// Above statement is right as it modifies the value which is allowed in the StringBuilder

2
欢迎来到stackoverflow。请在代码中包含对问题的解释,以及它是如何解决问题的。 - bad_coder

0

这样怎么样:

StringBuilder builder = new StringBuilder();
for(int i=99;i>=0;i--){
    builder.append(Integer.toString(i));
}
builder.toString();

或者

StringBuilder builder = new StringBuilder();
for(int i=0;i<100;i++){
  builder.insert(0, Integer.toString(i));
}
builder.toString();

但是这样做会使操作的时间复杂度从O(N)变为O(N^2)。

Java文档中的片段:

将Object参数的字符串表示形式插入到此字符序列中。总体效果就像第二个参数通过方法String.valueOf(Object)转换为字符串,然后该字符串的字符被插入到指定偏移量处的字符序列中一样。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接