Skip to content

Instantly share code, notes, and snippets.

@Miha-x64
Forked from zhong-j-yu/Stats.java
Last active February 1, 2022 12:08
Show Gist options
  • Save Miha-x64/406317855c0c91bfa66bd711bf22d1b6 to your computer and use it in GitHub Desktop.
Save Miha-x64/406317855c0c91bfa66bd711bf22d1b6 to your computer and use it in GitHub Desktop.
class Stats - used with Stream.collect() to find min and max
package net.aquadc.util;
import java.util.Comparator;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Collector;
/**
* Stats of a stream of objects, including {@linkplain #size()}, {@linkplain #min()}, and {@linkplain #max()}.
* <p>This is intended to be used with {@link java.util.stream.Stream#collect}, for example</p>
* <pre>
* Stats&lt;String&gt; stats = stringStream.collect(sizeMinMax());
* Stats&lt;String&gt; stringStream.collect(sizeMinMax(comparing(String::length)));
* </pre>
* <p>Original post and explanation on SO: http://stackoverflow.com/questions/41816264</p>
* <p>This fork: https://gist.github.com/Miha-x64/406317855c0c91bfa66bd711bf22d1b6</p>
* <p>License: Public Domain</p>
*/
public final class Stats<T> {
private final Comparator<? super T> comparator;
private long size;
private T min;
private T max;
private Stats(Comparator<? super T> comparator) {
this.comparator = comparator;
}
public long size() { return size; }
public Optional<T> min() { return size == 0 ? Optional.empty() : Optional.of(min); }
public Optional<T> max() { return size == 0 ? Optional.empty() : Optional.of(max); }
@Override public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Stats<?> stats = (Stats<?>) o;
return size == stats.size &&
comparator.equals(stats.comparator) &&
// with both.size == 0, min and max are gonna be uninitialized, be safe:
Objects.equals(min, stats.min) &&
Objects.equals(max, stats.max);
}
@Override public int hashCode() {
int result = comparator.hashCode();
if (size != 0) {
result = 31*result + Long.hashCode(size);
result = 31*result + min.hashCode();
result = 31*result + max.hashCode();
}
return result;
}
@Override public String toString() {
StringBuilder sb = new StringBuilder("Stats{");
if (size == 0) {
sb.append("empty");
} else {
sb.append("size=").append(size).append(", ").append("min=").append(min).append(", max=").append(max);
}
sb.append(", ").append("comparator=");
if (comparator instanceof Enum<?>) // Josh Bloch's singleton :'( e.g. NaturalOrderComparator
sb.append(comparator.getClass().getName()).append('.');
sb.append(comparator);
return sb.append('}').toString();
}
private void accept(T val) {
if (size == 0) min = max = val;
else if (comparator.compare(val, min) < 0) min = val;
else if (comparator.compare(val, max) > 0) max = val;
size++;
}
private Stats<T> combine(Stats<T> that) {
if (this.size == 0) return that;
else if (that.size == 0) return this;
else {
this.size += that.size;
if (comparator.compare(that.min, this.min) < 0) this.min = that.min;
if (comparator.compare(that.max, this.max) > 0) this.max = that.max;
return this;
}
}
public static <T> Collector<T, Stats<T>, Stats<T>> sizeMinMax(Comparator<? super T> comparator) {
return Collector.of(
() -> new Stats<>(comparator), Stats::accept, Stats::combine,
Collector.Characteristics.UNORDERED, Collector.Characteristics.IDENTITY_FINISH
);
}
public static <T extends Comparable<? super T>> Collector<T, Stats<T>, Stats<T>> sizeMinMax() {
return sizeMinMax(Comparator.naturalOrder());
}
/*public static void main(String[] args) {
System.out.println(java.util.stream.Stream.<String>empty().collect(sizeMinMax()));
Stats<Long> stats = java.util.stream.LongStream.rangeClosed(-123, +1_000_000).boxed().parallel().collect(sizeMinMax());
System.out.println(stats);
Comparator<Long> comparator = Comparator.<Long>naturalOrder().reversed();
stats = java.util.stream.LongStream.rangeClosed(-123, +1_000_000).boxed()
.parallel()
.collect(sizeMinMax(comparator));
System.out.println(stats);
}*/
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment