package ch.claude_martin.common; import java.util.Collection; import java.util.Collections; import java.util.Optional; 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; import java.util.stream.Collectors; import java.util.stream.Stream; import ch.claude_martin.common.ToOptional.Ref; /** * Collects a single element from a stream into an {@link Optional}. The returned Optional is empty if * there is not exactly one element in the stream. * *

* This is an alternative to {@link Stream#findAny()} as it checks that there is exactly one element * in the stream that can be found. It can also be seen as an alternative to the * {@link Collectors}, as this uses an Optional like a {@link Collection}, similar to * {@link Collectors#toList()}. *

* *

* Note that this might take a long time for streams with many elements. *

* *
 * Example:
 * List<Integer> list = Arrays.asList(1,2,3,4);
 * Optional<Integer> o = list.stream().filter(i->i==3).collect(ToOptional.get());
 * System.out.println(o); // Optional[3]
 * o = list.stream().filter(i->i>0).collect(ToOptional.get());
 * System.out.println(o); // Optional.empty
 * 
* * @see Collectors * @param * Type of the expected element. */ public final class ToOptional implements Collector, Optional> { private ToOptional() { super(); } public final static ToOptional INSTANCE = new ToOptional<>(); /** * Returns an instance of {@link ToOptional}. *

* Note: This always returns the same {@link Collector}. */ @SuppressWarnings("unchecked") public static ToOptional get() { return (ToOptional) INSTANCE; } static class Ref { private RT value; private int count = 0; public void set(RT value) { this.value = value; this.count++; } public RT get() { return this.value; } public int getCount() { return this.count; } } @Override public Supplier> supplier() { return Ref::new; } @Override public BiConsumer, T> accumulator() { return (ref, value) -> { ref.set(value); }; } @Override public BinaryOperator> combiner() { return (ref1, ref2) -> { if (ref1.getCount() == 0) return ref2; if (ref2.getCount() == 0) return ref1; ref1.set(null);// increments count return ref1; }; } @Override public Function, Optional> finisher() { return (ref) -> { if (ref.getCount() != 1) return Optional.empty(); return Optional.ofNullable(ref.get()); }; } @Override public Set characteristics() { return Collections.emptySet(); } }