为什么我们已经有了StringBuilder,还需要使用StringJoiner?

76

我最近遇到了一个Java 8类StringJoiner,它使用分隔符添加字符串,并添加前缀和后缀,但我不明白这个类的需求,因为它在后台也使用StringBuilder,并且执行很简单的字符串追加操作。

我没有真正理解这个类的实际目的吗?


6
实现简单并不排除其作为一个独立类时有用的可能性。特别是考虑到应用程序开发人员经常需要手动完成类似操作(或通过第三方库进行操作)。 - Holger
5个回答

79

StringJoiner非常有用,当你需要在Stream中连接字符串时。

例如,如果你有以下字符串列表:

final List<String> strings = Arrays.asList("Foo", "Bar", "Baz");

使用起来更简单

final String collectJoin = strings.stream().collect(Collectors.joining(", "));

就像使用StringBuilder一样:

final String collectBuilder =
    strings.stream().collect(Collector.of(StringBuilder::new,
        (stringBuilder, str) -> stringBuilder.append(str).append(", "),
        StringBuilder::append,
        StringBuilder::toString));

编辑六年后 正如评论中所指出的,现在有更简单的解决方案,比如String.join(", ", strings),这些在过去是不可用的。但使用情况仍然相同。


10
请注意,这两个示例_不相同_!第一个生成字符串“foo,Bar,Baz”,第二个生成“foo,Bar,Baz,”。 - Paul Wagland
3
是的,这就是.joining的额外之处。通常情况下,您不需要最后一个逗号... - Koshinae
2
@Koshinae 完全同意,即使在流式传输的情况下修复它也会使本来就复杂的语句变得更加复杂! - Paul Wagland
6
String.join(", ", strings)strings.stream().collect(Collectors.joining(", "))的更快版本。 - Nicolas Seiller
1
list.stream().map(Util::quote).collect(Collectors.joining(",")) 是什么意思? - Florian F

28

StringJoiner Javadoc的示例非常好地解释了这一点。整个重点是将分隔符的选择从添加条目的操作中抽象出来。例如,您可以创建一个连接器,指定要使用的分隔符,并将其传递给库来添加元素或反之亦然。

字符串 "[George:Sally:Fred]" 可以按以下方式构造:

StringJoiner sj = new StringJoiner(":", "[", "]");
sj.add("George").add("Sally").add("Fred");
String desiredString = sj.toString();

可以使用StringJoiner和Collectors.joining(CharSequence)方法从流(Stream)中创建格式化输出。例如:

List<Integer> numbers = Arrays.asList(1, 2, 3, 4);
String commaSeparatedNumbers = numbers.stream()
    .map(i -> i.toString())
    .collect(Collectors.joining(", "));

2
Peter,你有没有对StringBuilder和StringJoiner的性能进行过基准测试?就这方面而言,你更喜欢哪一个? - Simeon Leyzerzon
5
如果我想要在每个附加的字符串之间使用隐式分隔符,我会使用 String Joiner;如果不需要,则会使用 StringBuilder。这不是关于性能,而是为了使所需操作更清晰明确。 - Peter Lawrey

16

在某些使用情况下,它可能会简化您的代码:

List<String> list = // ...;

// with StringBuilder
StringBuilder builder = new StringBuilder();
builder.append("[");
if (!list.isEmpty()) {
    builder.append(list.get(0));
    for (int i = 1, n = list.size(); i < n; i++) {
        builder.append(",").append(list.get(i));
    }
}
builder.append("]");

// with StringJoiner
StringJoiner joiner = new StringJoiner(",", "[", "]");
for (String element : list) {
    joiner.add(element);
}

1
如果StringJoiner有一个接收List或Collection的方法,并在内部执行迭代和添加每个元素,那将是非常好的。 - chiperortiz

13
StringJoiner是一种Collector,虽然它没有实现Collector接口,但它的行为就像一个Collector。此外,你可以传递分隔符、前缀和后缀,StringJoiner可用于从Stream调用Collectors.joining(CharSequence)来创建格式化输出。
当使用并行流时,这种方法特别有用,因为并行处理的批次必须在某个时候合并,而这就是StringJoiner的用武之地。

8
不完全正确。它是一个低级实用组件,适合从收集器(事实上joining()就使用了它)中使用,但也适用于流之外的世界。人们时常编写这种类型的循环。 - Brian Goetz

7

StringJoiner比使用StringBuilder要简单得多。一个非常简单的代码就像

StringJoiner sj = new StringJoiner(",");
        sj.add("aaa");
        sj.add("bbb");
        sj.add("ccc");
        String result = sj.toString(); //aaa,bbb,ccc

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