Created
August 23, 2015 13:51
-
-
Save bnorm/2c97b4c6641b87b46e61 to your computer and use it in GitHub Desktop.
Allows collecting into 1 and only 1 object from a stream. Supports a few different finishing object types.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import java.util.Objects; | |
import java.util.Optional; | |
import java.util.StringJoiner; | |
import java.util.stream.Collector; | |
import java.util.stream.Collectors; | |
public class Singleton<T> { | |
private T value; | |
private volatile int count; | |
private final StringJoiner error; | |
private Singleton() { | |
this.value = null; | |
this.count = 0; | |
this.error = new StringJoiner(", ", "[", "]"); | |
} | |
private Singleton(T value) { | |
Objects.requireNonNull(value); | |
this.value = value; | |
this.count = 1; | |
this.error = new StringJoiner(", ", "[", "]"); | |
this.error.add(value.toString()); | |
} | |
public static <T> Singleton<T> of(T value) { | |
return new Singleton<>(value); | |
} | |
public T get() { | |
if (count == 0) { | |
throw new SingletonException("Singleton does not contain a value"); | |
} else if (count > 1) { | |
throw new SingletonException("Singleton contains [" + count + "] values: " + error); | |
} | |
assert count == 1; | |
return value; | |
} | |
public Optional<T> toOptional() { | |
if (count == 0) { | |
return Optional.empty(); | |
} else if (count > 1) { | |
throw new SingletonException("Singleton contains [" + count + "] values: " + error); | |
} | |
assert count == 1; | |
return Optional.of(value); | |
} | |
private Singleton<T> merge(Singleton<T> other) { | |
if (this.count == 0) { | |
// Identity function | |
return other; | |
} else if (other.count == 0) { | |
// Identity function | |
return this; | |
} | |
this.value = other.value; | |
this.error.merge(other.error); | |
this.count += other.count; // is this thread safe? | |
return this; | |
} | |
private void add(T value) { | |
this.value = value; | |
this.error.add(value.toString()); | |
this.count += 1; // is this thread safe? | |
} | |
public static <T> Collector<T, ?, Singleton<T>> collector() { | |
return Collector.of(Singleton::new, Singleton::add, Singleton::merge, Collector.Characteristics.UNORDERED, | |
Collector.Characteristics.CONCURRENT); | |
} | |
public static <T> Collector<T, ?, T> singleton() { | |
return Collectors.collectingAndThen(Singleton.collector(), Singleton::get); | |
} | |
public static <T> Collector<T, ?, Optional<T>> reduce() { | |
return Collectors.collectingAndThen(Singleton.collector(), Singleton::toOptional); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment