Suseika Suseika - 1 month ago 12
Java Question

How would you make a java.util.stream.Collector that collects to com.google.common.collect.ImmutableSet?

That seems too tricky for me since

ImmutableSet
instances are only built with
ImmutableSet.Builder
instances, which don't implement
Collection
so you can't just use
Collectors.toCollection(ImmutableSet::new)
or
Collectors.toCollection(ImmutableSet.Builder::new)
.

Answer

So it appears that writing a custom collector in this case is not such a difficult task as I imagined it to be:

package org.tendiwa.collections;

import com.google.common.collect.ImmutableSet;

import java.util.Set;
import java.util.function.BiConsumer;
import java.util.function.BinaryOperator;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collector;

public class ImmutableSetCollector<T> implements Collector<T, ImmutableSet.Builder<T>, ImmutableSet<T>> {

    @Override
    public Supplier<ImmutableSet.Builder<T>> supplier() {
        return ImmutableSet.Builder::new;
    }

    @Override
    public BiConsumer<ImmutableSet.Builder<T>, T> accumulator() {
        return (builder, element) -> builder.add(element);
    }

    @Override
    public BinaryOperator<ImmutableSet.Builder<T>> combiner() {
        return (b1, b2) -> b1.addAll(b2.build());
    }

    @Override
    public Function<ImmutableSet.Builder<T>, ImmutableSet<T>> finisher() {
        return ImmutableSet.Builder::build;
    }

    @Override
    public Set<Characteristics> characteristics() {
        return ImmutableSet.of();
    }
}

Usage:

public static void main(String[] args) {
    ImmutableSet<Point2D> set = Arrays.asList(
        new Point2D(1, 2),
        new Point2D(2, 4),
        new Point2D(3, 5),
        new Point2D(4, 4),
        new Point2D(5, 6),
        new Point2D(8, 6)
    ).stream().collect(new ImmutableSetCollector<>());
    System.out.println(set);
}

Output:

[{1.0:2.0}, {2.0:4.0}, {3.0:5.0}, {4.0:4.0}, {5.0:6.0}, {8.0:6.0}]