猿问

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

我有一个字符串流:


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>,它计算给定项目的数量,然后使用该计数。但我想知道是否有更直接的方法。


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


Smart猫小萌
浏览 377回答 2
2回答

慕村225694

是的,这可以使用自定义Collector实例来实现,该实例将使用带有流中项目计数的匿名对象和重载toString()方法:public String format(Stream<String> stream) {&nbsp; &nbsp; return stream.collect(&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; () -> new Object() {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; StringJoiner stringJoiner = new StringJoiner(",");&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; int count;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; @Override&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; public String toString() {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return count == 1 ? stringJoiner.toString() : "[" + stringJoiner + "]";&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; },&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; (container, currentString) -> {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; container.stringJoiner.add(currentString);&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; container.count++;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; },&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; (accumulatingContainer, currentContainer) -> {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; accumulatingContainer.stringJoiner.merge(currentContainer.stringJoiner);&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; accumulatingContainer.count += currentContainer.count;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;).toString();}解释Collector 接口有以下方法:public interface Collector<T,A,R> {&nbsp; &nbsp; Supplier<A> supplier();&nbsp; &nbsp; BiConsumer<A,T> accumulator();&nbsp; &nbsp; BinaryOperator<A> combiner();&nbsp; &nbsp; Function<A,R> finisher();&nbsp; &nbsp; Set<Characteristics> characteristics();}我将省略最后一个方法,因为它与本示例无关。有一个collect()具有以下签名的方法:<R> R collect(Supplier<R> supplier,&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; BiConsumer<R, ? super T> accumulator,&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; BiConsumer<R, R> combiner);在我们的情况下,它将解决:<Object> Object collect(Supplier<Object> supplier,&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; BiConsumer<Object, ? super String> accumulator,&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; BiConsumer<Object, Object> combiner);在 中supplier,我们使用了一个实例StringJoiner(与Collectors.joining()使用的东西基本相同)。在 中accumulator,我们正在使用StringJoiner::add()但我们也增加了计数在 中combiner,我们正在使用StringJoiner::merge()并将计数添加到累加器在从format()函数返回之前,我们需要调用toString()方法来包装我们累积的StringJoiner实例[](或者保持原样,在单元素流的情况下)也可以添加一个空箱子的箱子,为了不让这个收藏家更复杂,我把它省略了。

ITMISS

已经有一个可以接受的答案,我也赞成。我仍然想提供可能的另一种解决方案。可能是因为它有一个要求:the stream.spliterator()ofStream<String> stream需要是Spliterator.SIZED。如果这适用于您的情况,您也可以使用此解决方案:&nbsp; public String format(Stream<String> stream) {&nbsp; &nbsp; Spliterator<String> spliterator = stream.spliterator();&nbsp; &nbsp; StringJoiner sj = spliterator.getExactSizeIfKnown() == 1 ?&nbsp; &nbsp; &nbsp; new StringJoiner("") :&nbsp; &nbsp; &nbsp; new StringJoiner(",", "[", "]");&nbsp; &nbsp; spliterator.forEachRemaining(sj::add);&nbsp; &nbsp; return sj.toString();&nbsp; }根据JavaDoc Spliterator.getExactSizeIfKnown() “estimateSize()如果Spliterator是SIZED,则返回,否则返回-1。” 如果 aSpliterator是,SIZED则“estimateSize()在遍历或拆分之前表示有限大小,在没有结构源修改的情况下,表示完整遍历将遇到的元素数量的精确计数。”由于“大多数 Spliterators for Collections,涵盖了Collection报告此特征的所有元素”(JavaDoc 中的 API 注释SIZED),这可能是所需的更直接的方式。编辑:如果Stream是空的,我们可以立即返回一个空String的。如果Stream只有一个String,则无需创建一个StringJoiner并将其复制String到它。我们String直接返回单曲。&nbsp; public String format(Stream<String> stream) {&nbsp; &nbsp; Spliterator<String> spliterator = stream.spliterator();&nbsp; &nbsp; if (spliterator.getExactSizeIfKnown() == 0) {&nbsp; &nbsp; &nbsp; return "";&nbsp; &nbsp; }&nbsp; &nbsp; if (spliterator.getExactSizeIfKnown() == 1) {&nbsp; &nbsp; &nbsp; AtomicReference<String> result = new AtomicReference<String>();&nbsp; &nbsp; &nbsp; spliterator.tryAdvance(result::set);&nbsp; &nbsp; &nbsp; return result.get();&nbsp; &nbsp; }&nbsp; &nbsp; StringJoiner result = new StringJoiner(",", "[", "]");&nbsp; &nbsp; spliterator.forEachRemaining(result::add);&nbsp; &nbsp; return result.toString();&nbsp; }
随时随地看视频慕课网APP

相关分类

Java
我要回答