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> { |
|||
@ |
@Override |
||
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 |
|||
? ifEmpty.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 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> |