使用循环最佳实践创建StringBuilder

时间:2019-06-03 09:05:01

标签: java stringbuilder

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

还是通过将“”与当前项目连接在一起仍创建大量临时字符串?

如果可以的话,您可以建议使用更好的代码吗?

public String toString() {
    StringBuilder out = new StringBuilder();
    for (long item : someListOfNumbers) {
        out.append( " " + item);
    }
    return out.toString();
}

4 个答案:

答案 0 :(得分:4)

对于此特定用例,除非您的目标是在结果中留一个前导空格,否则您可能要使用StringJoiner

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

或使用Collectors.joining()

public String toString() {
    returns someListOfNumbers.stream()
        .map(String::valueOf)
        .collect(Collectors.joining(" "));
}

StringBuilder在构建文本时会更加高效,但是可以说上面的内容更具可读性。绝对可以避免在使用+时使用StringBuilder运算符,而应使用append()之类的方法。

如果您查看thisthis或其他答案,您会发现javac编译器还进一步优化了字符串连接,例如如果常量用+运算符连接,则编译器可以将结果常量插入字节码。

答案 1 :(得分:3)

out.append( " " + item);

在上面的语句中," " + item似乎存在问题,因为它的组合字符串带有长号。由于字符串是不可变的,因此字符串的+运算符会在内存中创建String的新副本。最佳解决方案可能是使用

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

甚至

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

在以上两种情况下,由于StringBuilder是Mutable的,因此不需要额外的内存空间来保存新的带修饰符的字符串。因此,它更理想。

答案 2 :(得分:1)

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

public String toString() {
    return someListOfNumbers.stream()
            .map(Number::toString)
            .collect(Collectors.joining(" "));
}

我假设someListOfNumbersList<Long>。我的方法给出的结果与您的结果不完全相同:您的结果有一个我遗漏的领先空间。如果需要该空间,可以自己添加或使用:

            .collect(Collectors.joining(" ", " ", ""));

由三个参数joining组成的分隔符,前缀和后缀按上述顺序(我将空字符串作为后缀)。

旁注:我曾经想使用.map(Long::toString),但由于其模棱两可而无法编译。这里的toString可以指代无参数的Long.toString或静态的一个参数Long.toString(long)。我通过LongNumber::toString的超类解决了这个问题。另一个解决方案是.map(String::valueOf),如Karol Dowbecki的回答。

答案 3 :(得分:0)

为了摆脱尾随或前导零,您必须检查循环中当前处理的项目的索引。

以下是三个示例,它们构建了一个String,此后没有任何内容可修剪:

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();
}

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