java - 仅当存在多个项目时才向 Collectors.joining() 添加前缀和后缀

标签 java string java-8 java-stream

我有一个字符串流:

Stream<String> stream = ...;

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

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

现在我想添加一个前缀 [和后缀 ]仅当有多个项目时才输出此输出。例如:

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

这可以在不首先实现 Stream<String> 的情况下完成吗?到 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(",")) + "]";
}

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

我可以实现自己的 Collector<String, String>它计算给定项目的数量并在之后使用该计数。但我想知道是否有更直接的方法。

这个问题有意忽略了流为空时的情况。

最佳答案

是的,这可以使用自定义 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();
}

说明

Collector接口(interface)有以下方法:

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

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

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

<R> R collect(Supplier<R> supplier,
              BiConsumer<R, ? super T> accumulator,
              BiConsumer<R, R> combiner);

在我们的例子中,它将解析为:

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

还可以添加一个空箱子的箱子,为了不让这个收集器变得更复杂,我把它省略了。

关于java - 仅当存在多个项目时才向 Collectors.joining() 添加前缀和后缀,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52672187/

相关文章:

java - Android UI 中的相对位置

java - 无法从 SD 卡访问数据库文件

java - 从以 "abc"开始并以 "def"结束的字符串获取字符串

java - 根据使用 Java 8 流的元素之间的差异拆分数字的有序列表多个列表

java - 标准 Kotlin 库中有哪些 Java 8 Stream.collect 等效项?

java - 如何在 Spring Boot 中将属性文件值读入字符串集

java - 从两个不同的接口(interface)调用相同的方法名称 - Java

java - 位图缩略图的延迟加载列表

android - 在 Android Studio 中评估字符串

Python:筛选充满 unicode 的列表并将列表仅包含字符串