Averages/Median: Difference between revisions

Content added Content deleted
Line 2,094: Line 2,094:


{{works with|Java|1.8+}}
{{works with|Java|1.8+}}

Written in the style of Java commonly found in real life, where annotations, tests, and abstractions get most of the real estate. The JavaDoc has been elided, or it'd be twice again as long. Java is verbose.
This version operates on objects rather than primitives and uses abstractions to operate on all of the standard numerics.


<lang java8>
<lang java8>
@FunctionalInterface
public class AveragesMedian {
interface MedianFinder<T, R> extends Function<Collection<T>, R> {


@FunctionalInterface
@Override
public interface MedianOfObjects<T, R> extends Function<Collection<T>, R> {
R apply(Collection<T> data);
}


@SuppressWarnings("ClassCanBeRecord")
@Override
class MedianFinderImpl<T, R> implements MedianFinder<T, R> {
R apply(Collection<T> data);
}


private final Supplier<R> ifEmpty;
protected abstract static class AbstractMedianOfObjectsImpl<T, R> implements MedianOfObjects<T, R> {
private final Function<T, R> ifOdd;
private final Function<List<T>, R> ifEven;


MedianFinderImpl(Supplier<R> ifEmpty, Function<T, R> ifOdd, Function<List<T>, R> ifEven) {
@SuppressWarnings("OptionalGetWithoutIsPresent")
this.ifEmpty = ifEmpty;
protected final R apply(Collection<T> data,
this.ifOdd = ifOdd;
Supplier<R> ifEmpty,
this.ifEven = ifEven;
Function<T, R> ifOdd,
}
Function<List<T>, R> ifEven) {


@Override
return Objects.requireNonNull(data, "data must not be null").isEmpty()
public R apply(Collection<T> data) {
? ifEmpty.get()
return Objects.requireNonNull(data, "data must not be null").isEmpty()
: (data.size() & 1) == 0
? ifEven.apply(data.stream().sorted().skip(data.size() / 2 - 1).limit(2).toList())
? ifEmpty.get()
: ifOdd.apply(data.stream().sorted().skip(data.size() / 2).limit(1).findFirst().get());
: (data.size() & 1) == 0
? ifEven.apply(data.stream().sorted()
}
.skip(data.size() / 2 - 1)
.limit(2).toList())
: ifOdd.apply(data.stream().sorted()
.skip(data.size() / 2)
.limit(1).findFirst().get());
}
}
}


public static class MedianOfFloats
public class MedianOf {
private static final MedianFinder<Integer, Integer> INTEGERS = new MedianFinderImpl<>(() -> 0, n -> n, pair -> (pair.get(0) + pair.get(1)) / 2);
extends AbstractMedianOfObjectsImpl<Float, Float> {
private static final MedianFinder<Integer, Float> INTEGERS_AS_FLOAT = new MedianFinderImpl<>(() -> 0f, n -> n * 1f, pair -> (pair.get(0) + pair.get(1)) / 2f);
private static final MedianFinder<Integer, Double> INTEGERS_AS_DOUBLE = new MedianFinderImpl<>(() -> 0d, n -> n * 1d, pair -> (pair.get(0) + pair.get(1)) / 2d);
private static final MedianFinder<Float, Float> FLOATS = new MedianFinderImpl<>(() -> 0f, n -> n, pair -> (pair.get(0) + pair.get(1)) / 2);
private static final MedianFinder<Double, Double> DOUBLES = new MedianFinderImpl<>(() -> 0d, n -> n, pair -> (pair.get(0) + pair.get(1)) / 2);
private static final MedianFinder<BigInteger, BigInteger> BIG_INTEGERS = new MedianFinderImpl<>(() -> BigInteger.ZERO, n -> n, pair -> pair.get(0).add(pair.get(1)).divide(BigInteger.TWO));
private static final MedianFinder<BigInteger, BigDecimal> BIG_INTEGERS_AS_BIG_DECIMAL = new MedianFinderImpl<>(() -> BigDecimal.ZERO, BigDecimal::new, pair -> new BigDecimal(pair.get(0).add(pair.get(1))).divide(BigDecimal.valueOf(2), RoundingMode.FLOOR));
private static final MedianFinder<BigDecimal, BigDecimal> BIG_DECIMALS = new MedianFinderImpl<>(() -> BigDecimal.ZERO, n -> n, pair -> pair.get(0).add(pair.get(1)).divide(BigDecimal.valueOf(2), RoundingMode.FLOOR));


public static Integer integers(Collection<Integer> integerCollection) {
private MedianOfFloats() {}
return INTEGERS.apply(integerCollection);

public static MedianOfFloats getInstance() {
return Holder.INSTANCE;
}

private static class Holder {
private static final MedianOfFloats INSTANCE = new MedianOfFloats();
}

@Override
public Float apply(Collection<Float> data) {
return apply(data, () -> 0f, n -> n, pair -> (pair.get(0) + pair.get(1)) / 2);
}
}
}
public static Float integersAsFloat(Collection<Integer> integerCollection) { return INTEGERS_AS_FLOAT.apply(integerCollection); }

public static Double integersAsDouble(Collection<Integer> integerCollection) { return INTEGERS_AS_DOUBLE.apply(integerCollection); }
private static class MedianOfFloatsTest {
public static Float floats(Collection<Float> floatCollection) {

return FLOATS.apply(floatCollection);
@Test
}
void getInstance() {
public static Double doubles(Collection<Double> doubleCollection) {
assertAll(() -> assertEquals(MedianOfFloats.getInstance(), MedianOfFloats.getInstance()),
return DOUBLES.apply(doubleCollection);
() -> assertNotNull(MedianOfFloats.getInstance()));
}

@Test
@SuppressWarnings("DuplicatedCode")
void apply() {
final MedianOfFloats instance = MedianOfFloats.getInstance();

assertThrows(NullPointerException.class, () -> instance.apply(null));

for (float a = -5f; a <= 5f; a += 0.4f) {
assertEquals(a, instance.apply(List.of(a)));

for (float b = -5; b <= 5; b += 0.4f) {
assertEquals((a + b) / 2f, instance.apply(List.of(a, b)));

for (float c = -5; c <=5; c += 0.4f) {
final List<Float> listOf3 = List.of(a, b, c);
final float medianOf3 = listOf3.stream().sorted().toList().get(1);
assertEquals(medianOf3, instance.apply(listOf3));

for (float d = -5; d <=5; d += 0.4f) {
final List<Float> listOf4 = List.of(a, b, c, d);
final List<Float> sortedListOf4 = listOf4.stream().sorted().toList();
final float medianOf4 = (sortedListOf4.get(1) + sortedListOf4.get(2)) / 2;
assertEquals(medianOf4, instance.apply(listOf4));

for (float e = -5; e <= 5; e += 0.4f) {
final List<Float> listOf5 = List.of(a, b, c, d, e);
final float medianOf5 = listOf5.stream().sorted().toList().get(2);
assertEquals(medianOf5, instance.apply(listOf5));
}
}
}
}
}
}
}
}
public static BigInteger bigIntegers(Collection<BigInteger> bigIntegerCollection) { return BIG_INTEGERS.apply(bigIntegerCollection); }
public static BigDecimal bigIntegersAsBigDecimal(Collection<BigInteger> bigIntegerCollection) { return BIG_INTEGERS_AS_BIG_DECIMAL.apply(bigIntegerCollection); }
public static BigDecimal bigDecimals(Collection<BigDecimal> bigDecimalCollection) { return BIG_DECIMALS.apply(bigDecimalCollection); }
}
}
</lang>
</lang>