仅当存在多个项目时,才将前缀和后缀添加到Collectors.joining()

Joh*_*Doe 6 java string java-8 java-stream

我有一些字符串:

Stream<String> stream = ...;
Run Code Online (Sandbox Code Playgroud)

我想构造一个字符串,将这些项目与,分隔符连接起来。我这样做如下:

stream.collect(Collectors.joining(","));
Run Code Online (Sandbox Code Playgroud)

现在,仅在有多个项目的情况下,才想在此输出中添加前缀[和后缀]。例如:

  • a
  • [a,b]
  • [a,b,c]

可以在不首先实现Stream<String>到a List<String>然后进行检查的情况下完成此操作List.size() == 1吗?在代码中:

public String format(Stream<String> stream) {
    List<String> list = stream.collect(Collectors.toList());

    if (list.size() == 1) {
        return list.get(0);
    }
    return "[" + list.stream().collect(Collectors.joining(",")) + "]";
}
Run Code Online (Sandbox Code Playgroud)

首先将流转换为列表,然后再次将其转换为流以能够应用,这感觉很奇怪Collectors.joining(",")。我认为遍历整个流(在期间完成Collectors.toList())只是发现是否存在一项或多项是次优的。

我可以实现自己的方法Collector<String, String>,对给定项目的数量进行计数,然后再使用该计数。但是我想知道是否有更直接的方法。

当流为空时,此问题有意忽略了这种情况。

syn*_*gma 6

是的,这可以通过使用自定义Collector实例来实现,该实例将使用匿名对象以及流中的项目计数和重载toString()方法:

public String format(Stream<String> stream) {
    return stream.collect(
            () -> new Object() {
                StringJoiner stringJoiner = new StringJoiner(",");
                int count;

                @Override
                public String toString() {
                    return count == 1 ? stringJoiner.toString() : "[" + stringJoiner + "]";
                }
            },
            (container, currentString) -> {
                container.stringJoiner.add(currentString);
                container.count++;
            },
            (accumulatingContainer, currentContainer) -> {
                accumulatingContainer.stringJoiner.merge(currentContainer.stringJoiner);
                accumulatingContainer.count += currentContainer.count;
            }
                         ).toString();
}
Run Code Online (Sandbox Code Playgroud)

说明

Collector 界面具有以下方法:

public interface Collector<T,A,R> {
    Supplier<A> supplier();
    BiConsumer<A,T> accumulator();
    BinaryOperator<A> combiner();
    Function<A,R> finisher();
    Set<Characteristics> characteristics();
}
Run Code Online (Sandbox Code Playgroud)

我将省略最后一个方法,因为它与本示例无关。

有一个collect()具有以下签名的方法:

<R> R collect(Supplier<R> supplier,
              BiConsumer<R, ? super T> accumulator,
              BiConsumer<R, R> combiner);
Run Code Online (Sandbox Code Playgroud)

在我们的情况下,它将解决:

<Object> Object collect(Supplier<Object> supplier,
              BiConsumer<Object, ? super String> accumulator,
              BiConsumer<Object, Object> combiner);
Run Code Online (Sandbox Code Playgroud)
  • 在中supplier,我们使用的实例StringJoiner(基本上与之相同Collectors.joining())。
  • 在中accumulator,我们正在使用,StringJoiner::add()但我们也会增加计数
  • 在中combiner,我们正在使用StringJoiner::merge()并将计数添加到累加器
  • format()函数返回之前,我们需要调用toString()方法将累积的StringJoiner实例包装在其中[](或在单元素流的情况下保留原样)

也可以添加一个空盒子的盒子,为了不使该收集器变得更加复杂,我将其省略。