Skip to content

Commit

Permalink
Expose more Java 8 APIs to Android users.
Browse files Browse the repository at this point in the history
RELNOTES=Exposed some additional Java 8 APIs to Android users.
PiperOrigin-RevId: 688998996
  • Loading branch information
cpovirk authored and Google Java Core Libraries committed Oct 23, 2024
1 parent 10c64d9 commit f9f3fff
Show file tree
Hide file tree
Showing 19 changed files with 574 additions and 16 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,15 @@
import static com.google.common.collect.Comparators.min;
import static com.google.common.truth.Truth.assertThat;
import static java.util.Arrays.asList;
import static java.util.Comparator.comparing;
import static java.util.Comparator.naturalOrder;

import com.google.common.annotations.GwtCompatible;
import com.google.common.collect.testing.Helpers;
import com.google.common.testing.EqualsTester;
import java.util.Collections;
import java.util.Comparator;
import java.util.Optional;
import junit.framework.TestCase;
import org.checkerframework.checker.nullness.qual.Nullable;

Expand Down Expand Up @@ -76,6 +79,30 @@ public void testIsInStrictOrder() {
assertTrue(Comparators.isInStrictOrder(Collections.<Integer>emptyList(), Ordering.natural()));
}

public void testEmptiesFirst() {
Optional<String> empty = Optional.empty();
Optional<String> abc = Optional.of("abc");
Optional<String> z = Optional.of("z");

Comparator<Optional<String>> comparator = Comparators.emptiesFirst(comparing(String::length));
Helpers.testComparator(comparator, empty, z, abc);

// Just demonstrate that no explicit type parameter is required
Comparator<Optional<String>> unused = Comparators.emptiesFirst(naturalOrder());
}

public void testEmptiesLast() {
Optional<String> empty = Optional.empty();
Optional<String> abc = Optional.of("abc");
Optional<String> z = Optional.of("z");

Comparator<Optional<String>> comparator = Comparators.emptiesLast(comparing(String::length));
Helpers.testComparator(comparator, z, abc, empty);

// Just demonstrate that no explicit type parameter is required
Comparator<Optional<String>> unused = Comparators.emptiesLast(naturalOrder());
}

public void testMinMaxNatural() {
assertThat(min(1, 2)).isEqualTo(1);
assertThat(min(2, 1)).isEqualTo(1);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,16 +36,25 @@
import static com.google.common.math.StatsTesting.MANY_VALUES_MEAN;
import static com.google.common.math.StatsTesting.MANY_VALUES_MIN;
import static com.google.common.math.StatsTesting.MANY_VALUES_SUM_OF_SQUARES_OF_DELTAS;
import static com.google.common.math.StatsTesting.MEGA_STREAM_COUNT;
import static com.google.common.math.StatsTesting.MEGA_STREAM_MAX;
import static com.google.common.math.StatsTesting.MEGA_STREAM_MEAN;
import static com.google.common.math.StatsTesting.MEGA_STREAM_MIN;
import static com.google.common.math.StatsTesting.MEGA_STREAM_POPULATION_VARIANCE;
import static com.google.common.math.StatsTesting.ONE_VALUE;
import static com.google.common.math.StatsTesting.OTHER_ONE_VALUE;
import static com.google.common.math.StatsTesting.TWO_VALUES;
import static com.google.common.math.StatsTesting.TWO_VALUES_MAX;
import static com.google.common.math.StatsTesting.TWO_VALUES_MEAN;
import static com.google.common.math.StatsTesting.TWO_VALUES_MIN;
import static com.google.common.math.StatsTesting.TWO_VALUES_SUM_OF_SQUARES_OF_DELTAS;
import static com.google.common.math.StatsTesting.megaPrimitiveDoubleStream;
import static com.google.common.math.StatsTesting.megaPrimitiveDoubleStreamPart1;
import static com.google.common.math.StatsTesting.megaPrimitiveDoubleStreamPart2;
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;
import static java.lang.Math.sqrt;
import static java.util.stream.DoubleStream.concat;
import static org.junit.Assert.assertThrows;

import com.google.common.collect.ImmutableList;
Expand Down Expand Up @@ -622,4 +631,51 @@ public void testMin() {
assertThat(longManyValuesAccumulatorByAddAllIterator.min()).isEqualTo(LONG_MANY_VALUES_MIN);
assertThat(longManyValuesAccumulatorByAddAllVarargs.min()).isEqualTo(LONG_MANY_VALUES_MIN);
}

public void testVerifyMegaStreamHalves() {
assertThat(
concat(megaPrimitiveDoubleStreamPart1(), megaPrimitiveDoubleStreamPart2())
.sorted()
.toArray())
.isEqualTo(megaPrimitiveDoubleStream().toArray());
}

public void testAddAllPrimitiveDoubleStream() {
StatsAccumulator accumulator = new StatsAccumulator();
accumulator.addAll(megaPrimitiveDoubleStreamPart1());
accumulator.addAll(megaPrimitiveDoubleStreamPart2());
assertThat(accumulator.count()).isEqualTo(MEGA_STREAM_COUNT);
assertThat(accumulator.mean()).isWithin(ALLOWED_ERROR * MEGA_STREAM_COUNT).of(MEGA_STREAM_MEAN);
assertThat(accumulator.populationVariance())
.isWithin(ALLOWED_ERROR * MEGA_STREAM_COUNT)
.of(MEGA_STREAM_POPULATION_VARIANCE);
assertThat(accumulator.min()).isEqualTo(MEGA_STREAM_MIN);
assertThat(accumulator.max()).isEqualTo(MEGA_STREAM_MAX);
}

public void testAddAllPrimitiveIntStream() {
StatsAccumulator accumulator = new StatsAccumulator();
accumulator.addAll(megaPrimitiveDoubleStreamPart1().mapToInt(x -> (int) x));
accumulator.addAll(megaPrimitiveDoubleStreamPart2().mapToInt(x -> (int) x));
assertThat(accumulator.count()).isEqualTo(MEGA_STREAM_COUNT);
assertThat(accumulator.mean()).isWithin(ALLOWED_ERROR * MEGA_STREAM_COUNT).of(MEGA_STREAM_MEAN);
assertThat(accumulator.populationVariance())
.isWithin(ALLOWED_ERROR * MEGA_STREAM_COUNT)
.of(MEGA_STREAM_POPULATION_VARIANCE);
assertThat(accumulator.min()).isEqualTo(MEGA_STREAM_MIN);
assertThat(accumulator.max()).isEqualTo(MEGA_STREAM_MAX);
}

public void testAddAllPrimitiveLongStream() {
StatsAccumulator accumulator = new StatsAccumulator();
accumulator.addAll(megaPrimitiveDoubleStreamPart1().mapToLong(x -> (long) x));
accumulator.addAll(megaPrimitiveDoubleStreamPart2().mapToLong(x -> (long) x));
assertThat(accumulator.count()).isEqualTo(MEGA_STREAM_COUNT);
assertThat(accumulator.mean()).isWithin(ALLOWED_ERROR * MEGA_STREAM_COUNT).of(MEGA_STREAM_MEAN);
assertThat(accumulator.populationVariance())
.isWithin(ALLOWED_ERROR * MEGA_STREAM_COUNT)
.of(MEGA_STREAM_POPULATION_VARIANCE);
assertThat(accumulator.min()).isEqualTo(MEGA_STREAM_MIN);
assertThat(accumulator.max()).isEqualTo(MEGA_STREAM_MAX);
}
}
101 changes: 101 additions & 0 deletions android/guava-tests/test/com/google/common/math/StatsTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

package com.google.common.math;

import static com.google.common.math.Stats.toStats;
import static com.google.common.math.StatsTesting.ALLOWED_ERROR;
import static com.google.common.math.StatsTesting.ALL_MANY_VALUES;
import static com.google.common.math.StatsTesting.ALL_STATS;
Expand Down Expand Up @@ -55,6 +56,11 @@
import static com.google.common.math.StatsTesting.MANY_VALUES_STATS_SNAPSHOT;
import static com.google.common.math.StatsTesting.MANY_VALUES_STATS_VARARGS;
import static com.google.common.math.StatsTesting.MANY_VALUES_SUM_OF_SQUARES_OF_DELTAS;
import static com.google.common.math.StatsTesting.MEGA_STREAM_COUNT;
import static com.google.common.math.StatsTesting.MEGA_STREAM_MAX;
import static com.google.common.math.StatsTesting.MEGA_STREAM_MEAN;
import static com.google.common.math.StatsTesting.MEGA_STREAM_MIN;
import static com.google.common.math.StatsTesting.MEGA_STREAM_POPULATION_VARIANCE;
import static com.google.common.math.StatsTesting.ONE_VALUE;
import static com.google.common.math.StatsTesting.ONE_VALUE_STATS;
import static com.google.common.math.StatsTesting.TWO_VALUES;
Expand All @@ -63,12 +69,14 @@
import static com.google.common.math.StatsTesting.TWO_VALUES_MIN;
import static com.google.common.math.StatsTesting.TWO_VALUES_STATS;
import static com.google.common.math.StatsTesting.TWO_VALUES_SUM_OF_SQUARES_OF_DELTAS;
import static com.google.common.math.StatsTesting.megaPrimitiveDoubleStream;
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;
import static java.lang.Double.NEGATIVE_INFINITY;
import static java.lang.Double.NaN;
import static java.lang.Double.POSITIVE_INFINITY;
import static java.lang.Math.sqrt;
import static java.util.Arrays.stream;
import static org.junit.Assert.assertThrows;

import com.google.common.collect.ImmutableList;
Expand All @@ -77,8 +85,10 @@
import com.google.common.primitives.Longs;
import com.google.common.testing.EqualsTester;
import com.google.common.testing.SerializableTester;
import java.math.BigDecimal;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.DoubleSummaryStatistics;
import junit.framework.TestCase;

/**
Expand Down Expand Up @@ -401,6 +411,39 @@ public void testMin() {
assertThat(LONG_MANY_VALUES_STATS_SNAPSHOT.min()).isEqualTo(LONG_MANY_VALUES_MIN);
}

public void testOfPrimitiveDoubleStream() {
Stats stats = Stats.of(megaPrimitiveDoubleStream());
assertThat(stats.count()).isEqualTo(MEGA_STREAM_COUNT);
assertThat(stats.mean()).isWithin(ALLOWED_ERROR * MEGA_STREAM_COUNT).of(MEGA_STREAM_MEAN);
assertThat(stats.populationVariance())
.isWithin(ALLOWED_ERROR * MEGA_STREAM_COUNT)
.of(MEGA_STREAM_POPULATION_VARIANCE);
assertThat(stats.min()).isEqualTo(MEGA_STREAM_MIN);
assertThat(stats.max()).isEqualTo(MEGA_STREAM_MAX);
}

public void testOfPrimitiveIntStream() {
Stats stats = Stats.of(megaPrimitiveDoubleStream().mapToInt(x -> (int) x));
assertThat(stats.count()).isEqualTo(MEGA_STREAM_COUNT);
assertThat(stats.mean()).isWithin(ALLOWED_ERROR * MEGA_STREAM_COUNT).of(MEGA_STREAM_MEAN);
assertThat(stats.populationVariance())
.isWithin(ALLOWED_ERROR * MEGA_STREAM_COUNT)
.of(MEGA_STREAM_POPULATION_VARIANCE);
assertThat(stats.min()).isEqualTo(MEGA_STREAM_MIN);
assertThat(stats.max()).isEqualTo(MEGA_STREAM_MAX);
}

public void testOfPrimitiveLongStream() {
Stats stats = Stats.of(megaPrimitiveDoubleStream().mapToLong(x -> (long) x));
assertThat(stats.count()).isEqualTo(MEGA_STREAM_COUNT);
assertThat(stats.mean()).isWithin(ALLOWED_ERROR * MEGA_STREAM_COUNT).of(MEGA_STREAM_MEAN);
assertThat(stats.populationVariance())
.isWithin(ALLOWED_ERROR * MEGA_STREAM_COUNT)
.of(MEGA_STREAM_POPULATION_VARIANCE);
assertThat(stats.min()).isEqualTo(MEGA_STREAM_MIN);
assertThat(stats.max()).isEqualTo(MEGA_STREAM_MAX);
}

public void testEqualsAndHashCode() {
new EqualsTester()
.addEqualityGroup(
Expand Down Expand Up @@ -523,4 +566,62 @@ public void testFromByteArrayWithTooShortArrayInputThrowsIllegalArgumentExceptio
.array();
assertThrows(IllegalArgumentException.class, () -> Stats.fromByteArray(tooShortByteArray));
}

public void testEquivalentStreams() {
// For datasets of many double values created from an array, we test many combinations of finite
// and non-finite values:
for (ManyValues values : ALL_MANY_VALUES) {
double[] array = values.asArray();
Stats stats = Stats.of(array);
// instance methods on Stats vs on instance methods on DoubleStream
assertThat(stats.count()).isEqualTo(stream(array).count());
assertEquivalent(stats.mean(), stream(array).average().getAsDouble());
assertEquivalent(stats.sum(), stream(array).sum());
assertEquivalent(stats.max(), stream(array).max().getAsDouble());
assertEquivalent(stats.min(), stream(array).min().getAsDouble());
// static method on Stats vs on instance method on DoubleStream
assertEquivalent(Stats.meanOf(array), stream(array).average().getAsDouble());
// instance methods on Stats vs instance methods on DoubleSummaryStatistics
DoubleSummaryStatistics streamStats = stream(array).summaryStatistics();
assertThat(stats.count()).isEqualTo(streamStats.getCount());
assertEquivalent(stats.mean(), streamStats.getAverage());
assertEquivalent(stats.sum(), streamStats.getSum());
assertEquivalent(stats.max(), streamStats.getMax());
assertEquivalent(stats.min(), streamStats.getMin());
}
}

private static void assertEquivalent(double actual, double expected) {
if (expected == POSITIVE_INFINITY) {
assertThat(actual).isPositiveInfinity();
} else if (expected == NEGATIVE_INFINITY) {
assertThat(actual).isNegativeInfinity();
} else if (Double.isNaN(expected)) {
assertThat(actual).isNaN();
} else {
assertThat(actual).isWithin(ALLOWED_ERROR).of(expected);
}
}

public void testBoxedDoubleStreamToStats() {
Stats stats = megaPrimitiveDoubleStream().boxed().collect(toStats());
assertThat(stats.count()).isEqualTo(MEGA_STREAM_COUNT);
assertThat(stats.mean()).isWithin(ALLOWED_ERROR * MEGA_STREAM_COUNT).of(MEGA_STREAM_MEAN);
assertThat(stats.populationVariance())
.isWithin(ALLOWED_ERROR * MEGA_STREAM_COUNT)
.of(MEGA_STREAM_POPULATION_VARIANCE);
assertThat(stats.min()).isEqualTo(MEGA_STREAM_MIN);
assertThat(stats.max()).isEqualTo(MEGA_STREAM_MAX);
}

public void testBoxedBigDecimalStreamToStats() {
Stats stats = megaPrimitiveDoubleStream().mapToObj(BigDecimal::valueOf).collect(toStats());
assertThat(stats.count()).isEqualTo(MEGA_STREAM_COUNT);
assertThat(stats.mean()).isWithin(ALLOWED_ERROR * MEGA_STREAM_COUNT).of(MEGA_STREAM_MEAN);
assertThat(stats.populationVariance())
.isWithin(ALLOWED_ERROR * MEGA_STREAM_COUNT)
.of(MEGA_STREAM_POPULATION_VARIANCE);
assertThat(stats.min()).isEqualTo(MEGA_STREAM_MIN);
assertThat(stats.max()).isEqualTo(MEGA_STREAM_MAX);
}
}
32 changes: 32 additions & 0 deletions android/guava-tests/test/com/google/common/math/StatsTesting.java
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
import com.google.common.primitives.Ints;
import java.math.BigInteger;
import java.util.List;
import java.util.stream.DoubleStream;

/**
* Inputs, expected outputs, and helper methods for tests of {@link StatsAccumulator}, {@link
Expand Down Expand Up @@ -212,6 +213,37 @@ private static ImmutableList<ManyValues> createAll() {
.divide(BigInteger.valueOf(16L))
.doubleValue();

/**
* Returns a stream of a million primitive doubles. The stream is parallel, which should cause
* {@code collect} calls to run in multithreaded mode, so testing the combiner as well as the
* supplier and accumulator.
*/
static DoubleStream megaPrimitiveDoubleStream() {
return DoubleStream.iterate(0.0, x -> x + 1.0).limit(MEGA_STREAM_COUNT).parallel();
}

/** Returns a stream containing half the values from {@link #megaPrimitiveDoubleStream}. */
static DoubleStream megaPrimitiveDoubleStreamPart1() {
return DoubleStream.iterate(0.0, x -> x + 2.0).limit(MEGA_STREAM_COUNT / 2).parallel();
}

/**
* Returns a stream containing the values from {@link #megaPrimitiveDoubleStream} not in {@link
* #megaPrimitiveDoubleStreamPart1()}.
*/
static DoubleStream megaPrimitiveDoubleStreamPart2() {
return DoubleStream.iterate(MEGA_STREAM_COUNT - 1.0, x -> x - 2.0)
.limit(MEGA_STREAM_COUNT / 2)
.parallel();
}

static final long MEGA_STREAM_COUNT = isAndroid() ? 100 : 1_000_000;
static final double MEGA_STREAM_MIN = 0.0;
static final double MEGA_STREAM_MAX = MEGA_STREAM_COUNT - 1;
static final double MEGA_STREAM_MEAN = MEGA_STREAM_MAX / 2;
static final double MEGA_STREAM_POPULATION_VARIANCE =
(MEGA_STREAM_COUNT - 1) * (MEGA_STREAM_COUNT + 1) / 12.0;

// Stats instances:

static final Stats EMPTY_STATS_VARARGS = Stats.of();
Expand Down
44 changes: 44 additions & 0 deletions android/guava/src/com/google/common/collect/Comparators.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,9 @@
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collector;
import javax.annotation.CheckForNull;
import org.checkerframework.checker.nullness.qual.Nullable;

/**
Expand Down Expand Up @@ -170,6 +172,48 @@ private Comparators() {}
return least(k, comparator.reversed());
}

/**
* Returns a comparator of {@link Optional} values which treats {@link Optional#empty} as less
* than all other values, and orders the rest using {@code valueComparator} on the contained
* value.
*
* @since NEXT (but since 22.0 in the JRE flavor)
*/
@SuppressWarnings("Java7ApiChecker")
@IgnoreJRERequirement // Users will use this only if they're already using Optional.
public static <T> Comparator<Optional<T>> emptiesFirst(Comparator<? super T> valueComparator) {
checkNotNull(valueComparator);
return Comparator.<Optional<T>, @Nullable T>comparing(
o -> orElseNull(o), Comparator.nullsFirst(valueComparator));
}

/**
* Returns a comparator of {@link Optional} values which treats {@link Optional#empty} as greater
* than all other values, and orders the rest using {@code valueComparator} on the contained
* value.
*
* @since NEXT (but since 22.0 in the JRE flavor)
*/
@SuppressWarnings("Java7ApiChecker")
@IgnoreJRERequirement // Users will use this only if they're already using Optional.
public static <T> Comparator<Optional<T>> emptiesLast(Comparator<? super T> valueComparator) {
checkNotNull(valueComparator);
return Comparator.<Optional<T>, @Nullable T>comparing(
o -> orElseNull(o), Comparator.nullsLast(valueComparator));
}

@SuppressWarnings("Java7ApiChecker")
@IgnoreJRERequirement // helper for emptiesFirst+emptiesLast
/*
* If we make these calls inline inside the lambda inside emptiesFirst()/emptiesLast(), we get an
* Animal Sniffer error, despite the @IgnoreJRERequirement annotation there. For details, see
* ImmutableSortedMultiset.
*/
@CheckForNull
private static <T> T orElseNull(Optional<T> optional) {
return optional.orElse(null);
}

/**
* Returns the minimum of the two values. If the values compare as 0, the first is returned.
*
Expand Down
Loading

0 comments on commit f9f3fff

Please sign in to comment.