diff options
Diffstat (limited to 'guava/src/com/google/common/collect/Maps.java')
-rw-r--r-- | guava/src/com/google/common/collect/Maps.java | 175 |
1 files changed, 82 insertions, 93 deletions
diff --git a/guava/src/com/google/common/collect/Maps.java b/guava/src/com/google/common/collect/Maps.java index 6265f5dce..173447caf 100644 --- a/guava/src/com/google/common/collect/Maps.java +++ b/guava/src/com/google/common/collect/Maps.java @@ -22,11 +22,12 @@ import static com.google.common.base.Predicates.compose; import static com.google.common.collect.CollectPreconditions.checkEntryNotNull; import static com.google.common.collect.CollectPreconditions.checkNonnegative; import static com.google.common.collect.NullnessCasts.uncheckedCastNullableTToT; +import static java.util.Collections.singletonMap; import static java.util.Objects.requireNonNull; -import com.google.common.annotations.Beta; import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.base.Converter; import com.google.common.base.Equivalence; import com.google.common.base.Function; @@ -37,6 +38,7 @@ import com.google.common.base.Predicates; import com.google.common.collect.MapDifference.ValueDifference; import com.google.common.primitives.Ints; import com.google.errorprone.annotations.CanIgnoreReturnValue; +import com.google.errorprone.annotations.concurrent.LazyInit; import com.google.j2objc.annotations.RetainedWith; import com.google.j2objc.annotations.Weak; import com.google.j2objc.annotations.WeakOuter; @@ -71,6 +73,7 @@ import java.util.function.BinaryOperator; import java.util.function.Consumer; import java.util.stream.Collector; import javax.annotation.CheckForNull; +import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.Nullable; /** @@ -153,6 +156,7 @@ public final class Maps { * @since 14.0 */ @GwtCompatible(serializable = true) + @J2ktIncompatible public static <K extends Enum<K>, V> ImmutableMap<K, V> immutableEnumMap( Map<K, ? extends V> map) { if (map instanceof ImmutableEnumMap) { @@ -168,9 +172,8 @@ public final class Maps { K key1 = entry1.getKey(); V value1 = entry1.getValue(); checkEntryNotNull(key1, value1); - Class<K> clazz = key1.getDeclaringClass(); - EnumMap<K, V> enumMap = new EnumMap<>(clazz); - enumMap.put(key1, value1); + // Do something that works for j2cl, where we can't call getDeclaredClass(): + EnumMap<K, V> enumMap = new EnumMap<>(singletonMap(key1, value1)); while (entryItr.hasNext()) { Entry<K, ? extends V> entry = entryItr.next(); K key = entry.getKey(); @@ -195,6 +198,7 @@ public final class Maps { * * @since 21.0 */ + @J2ktIncompatible public static <T extends @Nullable Object, K extends Enum<K>, V> Collector<T, ?, ImmutableMap<K, V>> toImmutableEnumMap( java.util.function.Function<? super T, ? extends K> keyFunction, @@ -213,6 +217,7 @@ public final class Maps { * * @since 21.0 */ + @J2ktIncompatible public static <T extends @Nullable Object, K extends Enum<K>, V> Collector<T, ?, ImmutableMap<K, V>> toImmutableEnumMap( java.util.function.Function<? super T, ? extends K> keyFunction, @@ -284,10 +289,19 @@ public final class Maps { return expectedSize + 1; } if (expectedSize < Ints.MAX_POWER_OF_TWO) { - // This is the calculation used in JDK8 to resize when a putAll - // happens; it seems to be the most conservative calculation we - // can make. 0.75 is the default load factor. - return (int) ((float) expectedSize / 0.75F + 1.0F); + // This seems to be consistent across JDKs. The capacity argument to HashMap and LinkedHashMap + // ends up being used to compute a "threshold" size, beyond which the internal table + // will be resized. That threshold is ceilingPowerOfTwo(capacity*loadFactor), where + // loadFactor is 0.75 by default. So with the calculation here we ensure that the + // threshold is equal to ceilingPowerOfTwo(expectedSize). There is a separate code + // path when the first operation on the new map is putAll(otherMap). There, prior to + // https://github.com/openjdk/jdk/commit/3e393047e12147a81e2899784b943923fc34da8e, a bug + // meant that sometimes a too-large threshold is calculated. However, this new threshold is + // independent of the initial capacity, except that it won't be lower than the threshold + // computed from that capacity. Because the internal table is only allocated on the first + // write, we won't see copying because of the new threshold. So it is always OK to use the + // calculation here. + return (int) Math.ceil(expectedSize / 0.75); } return Integer.MAX_VALUE; // any large value } @@ -469,28 +483,15 @@ public final class Maps { * @param right the map to treat as the "right" map for purposes of comparison * @return the difference between the two maps */ - @SuppressWarnings("unchecked") public static <K extends @Nullable Object, V extends @Nullable Object> MapDifference<K, V> difference( Map<? extends K, ? extends V> left, Map<? extends K, ? extends V> right) { if (left instanceof SortedMap) { + @SuppressWarnings("unchecked") SortedMap<K, ? extends V> sortedLeft = (SortedMap<K, ? extends V>) left; return difference(sortedLeft, right); } - /* - * This cast is safe: The Equivalence-accepting overload of difference() (which we call below) - * has a weird signature because Equivalence is itself a little weird. Still, we know that - * Equivalence.equals() can handle all inputs, and we know that the resulting MapDifference will - * contain only Ks and Vs (as opposed to possibly containing @Nullable objects even when K and V - * are *not* @Nullable). - * - * An alternative to suppressing the warning would be to inline the body of the other - * difference() method into this one. - */ - @SuppressWarnings("nullness") - MapDifference<K, V> result = - (MapDifference<K, V>) difference(left, right, Equivalence.equals()); - return result; + return difference(left, right, Equivalence.equals()); } /** @@ -507,36 +508,11 @@ public final class Maps { * @return the difference between the two maps * @since 10.0 */ - /* - * This method should really be annotated to accept maps with @Nullable value types. Fortunately, - * no existing Google callers appear to pass null values (much less pass null values *and* run a - * nullness checker). - * - * Still, if we decide that we want to make that work, we'd need to introduce a new type parameter - * for the Equivalence input type: - * - * <E, K extends @Nullable Object, V extends @Nullable E> ... difference(..., Equivalence<E> ...) - * - * Maybe we should, even though it will break source compatibility. - * - * Alternatively, this is a case in which it would be useful to be able to express Equivalence<? - * super @Nonnull T>). - * - * As things stand now, though, we have to either: - * - * - require non-null inputs so that we can guarantee non-null outputs - * - * - accept nullable inputs but force users to cope with nullable outputs - * - * And the non-null option is far more useful to existing users. - * - * (Vaguely related: Another thing we could consider is an overload that accepts a BiPredicate: - * https://github.com/google/guava/issues/3913) - */ - public static <K extends @Nullable Object, V> MapDifference<K, V> difference( - Map<? extends K, ? extends V> left, - Map<? extends K, ? extends V> right, - Equivalence<? super V> valueEquivalence) { + public static <K extends @Nullable Object, V extends @Nullable Object> + MapDifference<K, V> difference( + Map<? extends K, ? extends V> left, + Map<? extends K, ? extends V> right, + Equivalence<? super @NonNull V> valueEquivalence) { Preconditions.checkNotNull(valueEquivalence); Map<K, V> onlyOnLeft = newLinkedHashMap(); @@ -576,26 +552,14 @@ public final class Maps { SortedMap<K, V> onBoth = Maps.newTreeMap(comparator); SortedMap<K, MapDifference.ValueDifference<V>> differences = Maps.newTreeMap(comparator); - /* - * V is a possibly nullable type, but we decided to declare Equivalence with a type parameter - * that is restricted to non-nullable types. Still, this code is safe: We made that decision - * about Equivalence not because Equivalence is null-hostile but because *every* Equivalence can - * handle null inputs -- and thus it would be meaningless for the type system to distinguish - * between "an Equivalence for nullable Foo" and "an Equivalence for non-nullable Foo." - * - * (And the unchecked cast is safe because Equivalence is contravariant.) - */ - @SuppressWarnings({"nullness", "unchecked"}) - Equivalence<V> equalsEquivalence = (Equivalence<V>) Equivalence.equals(); - - doDifference(left, right, equalsEquivalence, onlyOnLeft, onlyOnRight, onBoth, differences); + doDifference(left, right, Equivalence.equals(), onlyOnLeft, onlyOnRight, onBoth, differences); return new SortedMapDifferenceImpl<>(onlyOnLeft, onlyOnRight, onBoth, differences); } private static <K extends @Nullable Object, V extends @Nullable Object> void doDifference( Map<? extends K, ? extends V> left, Map<? extends K, ? extends V> right, - Equivalence<? super V> valueEquivalence, + Equivalence<? super @NonNull V> valueEquivalence, Map<K, V> onlyOnLeft, Map<K, V> onlyOnRight, Map<K, V> onBoth, @@ -1329,14 +1293,26 @@ public final class Maps { * ... * ImmutableSet<Color> allColors = ImmutableSet.of(red, green, blue); * - * Map<String, Color> colorForName = - * uniqueIndex(allColors, toStringFunction()); + * ImmutableMap<String, Color> colorForName = + * uniqueIndex(allColors, c -> c.toString()); * assertThat(colorForName).containsEntry("red", red); * }</pre> * * <p>If your index may associate multiple values with each key, use {@link * Multimaps#index(Iterable, Function) Multimaps.index}. * + * <p><b>Note:</b> on Java 8 and later, it is usually better to use streams. For example: + * + * <pre>{@code + * import static com.google.common.collect.ImmutableMap.toImmutableMap; + * ... + * ImmutableMap<String, Color> colorForName = + * allColors.stream().collect(toImmutableMap(c -> c.toString(), c -> c)); + * }</pre> + * + * <p>Streams provide a more standard and flexible API and the lambdas make it clear what the keys + * and values in the map are. + * * @param values the values to use when constructing the {@code Map} * @param keyFunction the function used to produce the key for each value * @return a map mapping the result of evaluating the function {@code keyFunction} on each value @@ -1349,7 +1325,12 @@ public final class Maps { @CanIgnoreReturnValue public static <K, V> ImmutableMap<K, V> uniqueIndex( Iterable<V> values, Function<? super V, K> keyFunction) { - // TODO(lowasser): consider presizing the builder if values is a Collection + if (values instanceof Collection) { + return uniqueIndex( + values.iterator(), + keyFunction, + ImmutableMap.builderWithExpectedSize(((Collection<?>) values).size())); + } return uniqueIndex(values.iterator(), keyFunction); } @@ -1385,8 +1366,12 @@ public final class Maps { @CanIgnoreReturnValue public static <K, V> ImmutableMap<K, V> uniqueIndex( Iterator<V> values, Function<? super V, K> keyFunction) { + return uniqueIndex(values, keyFunction, ImmutableMap.builder()); + } + + private static <K, V> ImmutableMap<K, V> uniqueIndex( + Iterator<V> values, Function<? super V, K> keyFunction, ImmutableMap.Builder<K, V> builder) { checkNotNull(keyFunction); - ImmutableMap.Builder<K, V> builder = ImmutableMap.builder(); while (values.hasNext()) { V value = values.next(); builder.put(keyFunction.apply(value), value); @@ -1410,6 +1395,7 @@ public final class Maps { * @throws ClassCastException if any key in {@code properties} is not a {@code String} * @throws NullPointerException if any key or value in {@code properties} is null */ + @J2ktIncompatible @GwtIncompatible // java.util.Properties public static ImmutableMap<String, String> fromProperties(Properties properties) { ImmutableMap.Builder<String, String> builder = ImmutableMap.builder(); @@ -1481,7 +1467,7 @@ public final class Maps { /** * Returns an unmodifiable view of the specified map entry. The {@link Entry#setValue} operation - * throws an {@link UnsupportedOperationException}. This also has the side-effect of redefining + * throws an {@link UnsupportedOperationException}. This also has the side effect of redefining * {@code equals} to comply with the Entry contract, to avoid a possible nefarious implementation * of equals. * @@ -1522,7 +1508,7 @@ public final class Maps { }; } - /** @see Multimaps#unmodifiableEntries */ + /** The implementation of {@link Multimaps#unmodifiableEntries}. */ static class UnmodifiableEntries<K extends @Nullable Object, V extends @Nullable Object> extends ForwardingCollection<Entry<K, V>> { private final Collection<Entry<K, V>> entries; @@ -1544,15 +1530,14 @@ public final class Maps { // See java.util.Collections.UnmodifiableEntrySet for details on attacks. @Override - public Object[] toArray() { + public @Nullable Object[] toArray() { /* - * standardToArray returns `@Nullable Object[]` rather than `Object[]` but only because it can + * standardToArray returns `@Nullable Object[]` rather than `Object[]` but because it can * be used with collections that may contain null. This collection never contains nulls, so we - * can treat it as a plain `Object[]`. + * could return `Object[]`. But this class is private and J2KT cannot change return types in + * overrides, so we declare `@Nullable Object[]` as the return type. */ - @SuppressWarnings("nullness") - Object[] result = standardToArray(); - return result; + return standardToArray(); } @Override @@ -1562,7 +1547,7 @@ public final class Maps { } } - /** @see Maps#unmodifiableEntrySet(Set) */ + /** The implementation of {@link Maps#unmodifiableEntrySet(Set)}. */ static class UnmodifiableEntrySet<K extends @Nullable Object, V extends @Nullable Object> extends UnmodifiableEntries<K, V> implements Set<Entry<K, V>> { UnmodifiableEntrySet(Set<Entry<K, V>> entries) { @@ -1692,13 +1677,15 @@ public final class Maps { return new UnmodifiableBiMap<>(bimap, null); } - /** @see Maps#unmodifiableBiMap(BiMap) */ + /** + * @see Maps#unmodifiableBiMap(BiMap) + */ private static class UnmodifiableBiMap<K extends @Nullable Object, V extends @Nullable Object> extends ForwardingMap<K, V> implements BiMap<K, V>, Serializable { final Map<K, V> unmodifiableMap; final BiMap<? extends K, ? extends V> delegate; - @RetainedWith @CheckForNull BiMap<V, K> inverse; - @CheckForNull transient Set<V> values; + @LazyInit @RetainedWith @CheckForNull BiMap<V, K> inverse; + @LazyInit @CheckForNull transient Set<V> values; UnmodifiableBiMap(BiMap<? extends K, ? extends V> delegate, @CheckForNull BiMap<V, K> inverse) { unmodifiableMap = Collections.unmodifiableMap(delegate); @@ -1763,8 +1750,9 @@ public final class Maps { } @Override + @CheckForNull public V merge( - K key, V value, BiFunction<? super V, ? super V, ? extends V> remappingFunction) { + K key, V value, BiFunction<? super V, ? super V, ? extends @Nullable V> function) { throw new UnsupportedOperationException(); } @@ -2103,6 +2091,7 @@ public final class Maps { * @throws NullPointerException if the key or value is null and this transformer does not accept * null arguments */ + @ParametricNullness V2 transformEntry(@ParametricNullness K key, @ParametricNullness V1 value); } @@ -3668,12 +3657,13 @@ public final class Maps { } @Override + @CheckForNull public V merge( - K key, V value, BiFunction<? super V, ? super V, ? extends V> remappingFunction) { + K key, V value, BiFunction<? super V, ? super V, ? extends @Nullable V> function) { throw new UnsupportedOperationException(); } - @CheckForNull private transient UnmodifiableNavigableMap<K, V> descendingMap; + @LazyInit @CheckForNull private transient UnmodifiableNavigableMap<K, V> descendingMap; @Override public NavigableMap<K, V> descendingMap() { @@ -3803,7 +3793,7 @@ public final class Maps { */ abstract Set<Entry<K, V>> createEntrySet(); - @CheckForNull private transient Set<Entry<K, V>> entrySet; + @LazyInit @CheckForNull private transient Set<Entry<K, V>> entrySet; @Override public Set<Entry<K, V>> entrySet() { @@ -3811,7 +3801,7 @@ public final class Maps { return (result == null) ? entrySet = createEntrySet() : result; } - @CheckForNull private transient Set<K> keySet; + @LazyInit @CheckForNull private transient Set<K> keySet; @Override public Set<K> keySet() { @@ -3823,7 +3813,7 @@ public final class Maps { return new KeySet<>(this); } - @CheckForNull private transient Collection<V> values; + @LazyInit @CheckForNull private transient Collection<V> values; @Override public Collection<V> values() { @@ -4385,7 +4375,7 @@ public final class Maps { return forward(); } - @CheckForNull private transient Comparator<? super K> comparator; + @LazyInit @CheckForNull private transient Comparator<? super K> comparator; @SuppressWarnings("unchecked") @Override @@ -4495,7 +4485,7 @@ public final class Maps { return forward(); } - @CheckForNull private transient Set<Entry<K, V>> entrySet; + @LazyInit @CheckForNull private transient Set<Entry<K, V>> entrySet; @Override public Set<Entry<K, V>> entrySet() { @@ -4526,7 +4516,7 @@ public final class Maps { return navigableKeySet(); } - @CheckForNull private transient NavigableSet<K> navigableKeySet; + @LazyInit @CheckForNull private transient NavigableSet<K> navigableKeySet; @Override public NavigableSet<K> navigableKeySet() { @@ -4610,7 +4600,6 @@ public final class Maps { * * @since 20.0 */ - @Beta @GwtIncompatible // NavigableMap public static <K extends Comparable<? super K>, V extends @Nullable Object> NavigableMap<K, V> subMap(NavigableMap<K, V> map, Range<K> range) { |