Да, это возможно при использовании пользовательского экземпляра 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
в []
(или оставить его как есть, в случае одноэлементного потока
Также можно добавить футляр для пустого футляра, я оставил его, чтобы не усложнять этот коллектор.