Java 使用循环最佳实践创建StringBuilder

Java 使用循环最佳实践创建StringBuilder,java,stringbuilder,Java,Stringbuilder,我想知道这段代码是否表明使用StringBuilder是最有效的 或者它仍然通过连接“”和当前项来创建大量临时字符串 如果是的话,你能推荐更好的代码吗 public String toString() { StringBuilder out = new StringBuilder(); for (long item : someListOfNumbers) { out.append( " " + item); } return out.toStri

我想知道这段代码是否表明使用StringBuilder是最有效的

或者它仍然通过连接“”和当前项来创建大量临时字符串

如果是的话,你能推荐更好的代码吗

public String toString() {
    StringBuilder out = new StringBuilder();
    for (long item : someListOfNumbers) {
        out.append( " " + item);
    }
    return out.toString();
}
在上面的语句中,
“+item
的组合字符串与长。由于字符串是不可变的,所以字符串的
+
运算符在内存中创建字符串的新副本。最佳解决方案可以是使用

out.append(' ').append(item);
甚至

out.append(" ").append(item);

在上述两种情况下,由于StringBuilder是可变的,因此不需要额外的内存空间来保存新的关联字符串。因此它更为优化。

对于这个您可能想要使用的特定用例,除非您的目标是在结果中有一个领先的空间

public String toString() {
    StringJoiner joiner = new StringJoiner(" ");
    someListOfNumbers.forEach(l -> joiner.add(Long.toString(l)));
    return joiner.toString();
}
或使用:

StringBuilder
在构建文本时将是一个更有效的方法,但可以说上面的方法更具可读性。使用
StringBuilder
时,一定要避免使用
+
运算符,而是使用
append()
等方法


如果您查看或或其他答案,您将看到
javac
编译器对字符串连接进行了额外优化,例如,如果常量与
+
运算符连接,则编译器可以将生成的常量插入字节码。

以消除尾随或前导零,您必须检查循环中当前处理的项的索引

下面是三个示例,它们构建了一个
字符串
,之后没有任何要修剪的内容:

public String toString() {
    StringBuilder out = new StringBuilder();
    // iterate in a classic for-loop checking for item at index 0
    for (int i = 0; i < someListOfNumbers.size(); i++) {
        long item = someListOfNumbers.get(i);
        if (i == 0) {
            // in this case, avoid a leading zero
            out.append(item);
        } else {
            out.append(" ").append(item);
        }
    }
    return out.toString();
}

public String toString() {
    StringBuilder out = new StringBuilder();
    // iterate in a classic for-loop checking for item at last index
    for (int i = 0; i < someListOfNumbers.size(); i++) {
        long item = someListOfNumbers.get(i);
        if (i == someListOfNumbers.size() - 1) {
            out.append(item).append(" ");
        } else {
            // this case avoids a trailing zero
            out.append(item);
        }
    }
    return out.toString();
}

public String toString() {
    StringBuilder out = new StringBuilder();
    // add the first element and iterate from 1 to end afterwards 
    // (this requires a check for an empty list)
    out.append(someListOfNumbers.get(0));
    for (int i = 1; i < someListOfNumbers.size(); i++) {
        out.append(" ").append(someListOfNumbers.get(i));
    }
    return out.toString();
}
公共字符串toString(){
StringBuilder out=新的StringBuilder();
//在索引0处的项的经典for循环检查中迭代
for(int i=0;i

您甚至可以使用自己的方法,但是使用增强的
for
-循环,您必须使用
indexOf
然后执行检查。我不希望这样,但这是你的决定。

现代的方法将字符串生成器的操作留给库方法:

public String toString() {
    return someListOfNumbers.stream()
            .map(Number::toString)
            .collect(Collectors.joining(" "));
}
我假设
someListOfNumbers
是一个
列表
。我的方法给出的结果与你的不完全相同:你的结果有一个前导空格,我忽略了它。如果您需要该空间,您可以自己预先设置或使用:

            .collect(Collectors.joining(" ", " ", ""));
三个arg
连接
需要按上述顺序使用分隔符、前缀和后缀(我将空字符串作为后缀)


旁注:我本来想使用
.map(Long::toString)
,但由于不明确,所以无法编译。这里的
toString
可能指的是无参数
Long.toString
或静态参数
Long.toString(Long)
。我通过超类
Long
Number::toString
解决了这个问题。另一个解决方案是
.map(String::valueOf)
,就像Karol Dowbecki的回答一样。

out.append(“”).append(item)?这将导致前导空格,我不知道是否需要。也可以预先将StringBuilder的容量调整到某个估计值(
someListOfNumbers.size()*4
或诸如此类),以避免重复调整大小。对,但这只有在我们现在确定结果字符串中字符的确切长度时才可能,否则它可能会导致异常。@AsadChoudhary不,它不会导致异常,它只会调整大小。我的意思是,如果字符长度从给定的大小增加,则会出现索引越界异常。不是吗?@AsadChoudhary不,它只会调整大小。
joiner::add
如果
someListOfNumbers
实际上是一个数字列表,它将不起作用。StringJoiner需要您首先将所有数字转换为中间字符串,而StringBuilder可以直接将数字输出到其内部缓冲区,因此我使用StringBuilder(无论如何都不会使代码变短)。我更喜欢三个示例中的第三个。在
for
循环中有一个特例是有点,只是有点反模式,在这种情况下很容易避免。@OleV.V.当然,在每种情况下都应该避免反模式,无论它们有多大;-)但是在这种情况下,我认为这是关于正确工作的代码,而不是代码质量。我同意反模式,我可能在这里使用了一个强有力的词。我倾向于不同意它是关于什么的:对我来说,它总是关于代码质量。谢谢你的评论。@OleV.V。我毫不怀疑,现在对您来说,代码质量一直是个问题,这很好。但是,有一段时间你不知道代码质量,只想学习基础知识吗?我真的不知道OP想要什么,但似乎主要不是代码质量,而是满足某些功能要求的代码。@OleV.V这绝对是教授编程的正确方法。对你的学生有好处
public String toString() {
    return someListOfNumbers.stream()
            .map(Number::toString)
            .collect(Collectors.joining(" "));
}
            .collect(Collectors.joining(" ", " ", ""));