为了账号安全,请及时绑定邮箱和手机立即绑定

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

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

Smart猫小萌 2021-10-28 16:42:27
我有一个字符串流:Stream<String> stream = ...;我想构造一个将这些项目连接起来,作为分隔符的字符串。我这样做如下:stream.collect(Collectors.joining(","));现在我只想在有多个项目时为这个输出添加一个前缀[和一个后缀]。例如:a[a,b][a,b,c]这可以在不先实现Stream<String>to aList<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(","). 我认为循环遍历整个流(在 a 期间完成Collectors.toList())只是为了发现是否存在一个或多个项目是次优的。我可以实现我自己的Collector<String, String>,它计算给定项目的数量,然后使用该计数。但我想知道是否有更直接的方法。当流为空时,这个问题有意忽略了这种情况。
查看完整描述

2 回答

?
慕村225694

TA贡献1880条经验 获得超4个赞

是的,这可以使用自定义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 接口有以下方法:


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()使用的东西基本相同)。

  • 在 中accumulator,我们正在使用StringJoiner::add()但我们也增加了计数

  • 在 中combiner,我们正在使用StringJoiner::merge()并将计数添加到累加器

  • 在从format()函数返回之前,我们需要调用toString()方法来包装我们累积的StringJoiner实例[](或者保持原样,在单元素流的情况下)

也可以添加一个空箱子的箱子,为了不让这个收藏家更复杂,我把它省略了。


查看完整回答
反对 回复 2021-10-28
?
ITMISS

TA贡献1871条经验 获得超8个赞

已经有一个可以接受的答案,我也赞成。


我仍然想提供可能的另一种解决方案。可能是因为它有一个要求:

the stream.spliterator()ofStream<String> stream需要是Spliterator.SIZED。


如果这适用于您的情况,您也可以使用此解决方案:


  public String format(Stream<String> stream) {

    Spliterator<String> spliterator = stream.spliterator();

    StringJoiner sj = spliterator.getExactSizeIfKnown() == 1 ?

      new StringJoiner("") :

      new StringJoiner(",", "[", "]");

    spliterator.forEachRemaining(sj::add);


    return sj.toString();

  }

根据JavaDoc Spliterator.getExactSizeIfKnown() “estimateSize()如果Spliterator是SIZED,则返回,否则返回-1。” 如果 aSpliterator是,SIZED则“estimateSize()在遍历或拆分之前表示有限大小,在没有结构源修改的情况下,表示完整遍历将遇到的元素数量的精确计数。”


由于“大多数 Spliterators for Collections,涵盖了Collection报告此特征的所有元素”(JavaDoc 中的 API 注释SIZED),这可能是所需的更直接的方式。


编辑:

如果Stream是空的,我们可以立即返回一个空String的。如果Stream只有一个String,则无需创建一个StringJoiner并将其复制String到它。我们String直接返回单曲。


  public String format(Stream<String> stream) {

    Spliterator<String> spliterator = stream.spliterator();


    if (spliterator.getExactSizeIfKnown() == 0) {

      return "";

    }


    if (spliterator.getExactSizeIfKnown() == 1) {

      AtomicReference<String> result = new AtomicReference<String>();

      spliterator.tryAdvance(result::set);

      return result.get();

    }


    StringJoiner result = new StringJoiner(",", "[", "]");

    spliterator.forEachRemaining(result::add);

    return result.toString();

  }


查看完整回答
反对 回复 2021-10-28
  • 2 回答
  • 0 关注
  • 376 浏览

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信