Skip to content

Instantly share code, notes, and snippets.

@bnorm
Created August 23, 2015 13:51
Show Gist options
  • Save bnorm/2c97b4c6641b87b46e61 to your computer and use it in GitHub Desktop.
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.
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