Skip to content

Commit

Permalink
Use a Map<K, ImmutableCollection.Builder<V>> instead of Map<K, Collec…
Browse files Browse the repository at this point in the history
…tion<V>> in ImmutableMultimap.Builder.

RELNOTES=n/a
PiperOrigin-RevId: 643464920
  • Loading branch information
lowasser authored and Google Java Core Libraries committed Jun 14, 2024
1 parent 95b394c commit 040d0b9
Show file tree
Hide file tree
Showing 9 changed files with 185 additions and 70 deletions.
15 changes: 15 additions & 0 deletions android/guava/src/com/google/common/collect/ImmutableList.java
Original file line number Diff line number Diff line change
Expand Up @@ -849,6 +849,21 @@ public ImmutableList<E> build() {
forceCopy = true;
return asImmutableList(contents, size);
}

/**
* Returns a newly-created {@code ImmutableList} based on the contents of the {@code Builder},
* sorted according to the specified comparator.
*/
@SuppressWarnings("unchecked")
ImmutableList<E> buildSorted(Comparator<? super E> comparator) {
// Currently only used by ImmutableListMultimap.Builder.orderValuesBy.
// In particular, this implies that the comparator can never get "removed," so this can't
// invalidate future builds.

forceCopy = true;
Arrays.sort((E[]) contents, 0, size, comparator);
return asImmutableList(contents, size);
}
}

private static final long serialVersionUID = 0xcafebabe;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -383,6 +383,29 @@ static <K, V> ImmutableListMultimap<K, V> fromMapEntries(
return new ImmutableListMultimap<>(builder.buildOrThrow(), size);
}

/** Creates an ImmutableListMultimap from an asMap.entrySet. */
static <K, V> ImmutableListMultimap<K, V> fromMapBuilderEntries(
Collection<? extends Map.Entry<K, ImmutableCollection.Builder<V>>> mapEntries,
@CheckForNull Comparator<? super V> valueComparator) {
if (mapEntries.isEmpty()) {
return of();
}
ImmutableMap.Builder<K, ImmutableList<V>> builder =
new ImmutableMap.Builder<>(mapEntries.size());
int size = 0;

for (Entry<K, ImmutableCollection.Builder<V>> entry : mapEntries) {
K key = entry.getKey();
ImmutableList.Builder<V> values = (ImmutableList.Builder<V>) entry.getValue();
ImmutableList<V> list =
(valueComparator == null) ? values.build() : values.buildSorted(valueComparator);
builder.put(key, list);
size += list.size();
}

return new ImmutableListMultimap<>(builder.buildOrThrow(), size);
}

ImmutableListMultimap(ImmutableMap<K, ImmutableList<V>> map, int size) {
super(map, size);
}
Expand Down
50 changes: 20 additions & 30 deletions android/guava/src/com/google/common/collect/ImmutableMultimap.java
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@
import java.io.InvalidObjectException;
import java.io.ObjectInputStream;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Comparator;
Expand Down Expand Up @@ -149,7 +148,7 @@ public static <K, V> Builder<K, V> builder() {
*/
@DoNotMock
public static class Builder<K, V> {
@CheckForNull Map<K, Collection<V>> builderMap;
@CheckForNull Map<K, ImmutableCollection.Builder<V>> builderMap;
@CheckForNull Comparator<? super K> keyComparator;
@CheckForNull Comparator<? super V> valueComparator;

Expand All @@ -159,29 +158,28 @@ public static class Builder<K, V> {
*/
public Builder() {}

Map<K, Collection<V>> ensureBuilderMapNonNull() {
Map<K, Collection<V>> result = builderMap;
Map<K, ImmutableCollection.Builder<V>> ensureBuilderMapNonNull() {
Map<K, ImmutableCollection.Builder<V>> result = builderMap;
if (result == null) {
result = Platform.preservesInsertionOrderOnPutsMap();
builderMap = result;
}
return result;
}

Collection<V> newMutableValueCollection() {
return new ArrayList<>();
ImmutableCollection.Builder<V> newValueCollectionBuilder() {
return ImmutableList.builder();
}

/** Adds a key-value mapping to the built multimap. */
@CanIgnoreReturnValue
public Builder<K, V> put(K key, V value) {
checkEntryNotNull(key, value);
Map<K, Collection<V>> builderMap = ensureBuilderMapNonNull();
Collection<V> valueCollection = builderMap.get(key);
if (valueCollection == null) {
builderMap.put(key, valueCollection = newMutableValueCollection());
ImmutableCollection.Builder<V> valuesBuilder = ensureBuilderMapNonNull().get(key);
if (valuesBuilder == null) {
builderMap.put(key, valuesBuilder = newValueCollectionBuilder());
}
valueCollection.add(value);
valuesBuilder.add(value);
return this;
}

Expand Down Expand Up @@ -219,26 +217,20 @@ public Builder<K, V> putAll(K key, Iterable<? extends V> values) {
if (key == null) {
throw new NullPointerException("null key in entry: null=" + Iterables.toString(values));
}
Map<K, Collection<V>> builderMap = ensureBuilderMapNonNull();
Collection<V> valueCollection = builderMap.get(key);
if (valueCollection != null) {
for (V value : values) {
checkEntryNotNull(key, value);
valueCollection.add(value);
}
return this;
}
Iterator<? extends V> valuesItr = values.iterator();
if (!valuesItr.hasNext()) {
return this;
}
valueCollection = newMutableValueCollection();
ImmutableCollection.Builder<V> valuesBuilder = ensureBuilderMapNonNull().get(key);
if (valuesBuilder == null) {
valuesBuilder = newValueCollectionBuilder();
builderMap.put(key, valuesBuilder);
}
while (valuesItr.hasNext()) {
V value = valuesItr.next();
checkEntryNotNull(key, value);
valueCollection.add(value);
valuesBuilder.add(value);
}
builderMap.put(key, valueCollection);
return this;
}

Expand Down Expand Up @@ -294,26 +286,24 @@ public Builder<K, V> orderValuesBy(Comparator<? super V> valueComparator) {

@CanIgnoreReturnValue
Builder<K, V> combine(Builder<K, V> other) {
Map<K, Collection<V>> otherBuilderMap = other.builderMap;
if (otherBuilderMap != null) {
for (Map.Entry<K, Collection<V>> entry : otherBuilderMap.entrySet()) {
putAll(entry.getKey(), entry.getValue());
if (other.builderMap != null) {
for (Map.Entry<K, ImmutableCollection.Builder<V>> entry : other.builderMap.entrySet()) {
putAll(entry.getKey(), entry.getValue().build());
}
}
return this;
}

/** Returns a newly-created immutable multimap. */
public ImmutableMultimap<K, V> build() {
Map<K, Collection<V>> builderMap = this.builderMap;
if (builderMap == null) {
return ImmutableListMultimap.of();
}
Collection<Map.Entry<K, Collection<V>>> mapEntries = builderMap.entrySet();
Collection<Map.Entry<K, ImmutableCollection.Builder<V>>> mapEntries = builderMap.entrySet();
if (keyComparator != null) {
mapEntries = Ordering.from(keyComparator).<K>onKeys().immutableSortedCopy(mapEntries);
}
return ImmutableListMultimap.fromMapEntries(mapEntries, valueComparator);
return ImmutableListMultimap.fromMapBuilderEntries(mapEntries, valueComparator);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -253,8 +253,10 @@ public Builder() {
}

@Override
Collection<V> newMutableValueCollection() {
return Platform.preservesInsertionOrderOnAddsSet();
ImmutableCollection.Builder<V> newValueCollectionBuilder() {
return (valueComparator == null)
? ImmutableSet.builder()
: new ImmutableSortedSet.Builder<V>(valueComparator);
}

/** Adds a key-value mapping to the built multimap if it is not already present. */
Expand Down Expand Up @@ -352,15 +354,14 @@ public Builder<K, V> orderValuesBy(Comparator<? super V> valueComparator) {
/** Returns a newly-created immutable set multimap. */
@Override
public ImmutableSetMultimap<K, V> build() {
Map<K, Collection<V>> builderMap = this.builderMap;
if (builderMap == null) {
return ImmutableSetMultimap.of();
}
Collection<Map.Entry<K, Collection<V>>> mapEntries = builderMap.entrySet();
Collection<Map.Entry<K, ImmutableCollection.Builder<V>>> mapEntries = builderMap.entrySet();
if (keyComparator != null) {
mapEntries = Ordering.from(keyComparator).<K>onKeys().immutableSortedCopy(mapEntries);
}
return fromMapEntries(mapEntries, valueComparator);
return fromMapBuilderEntries(mapEntries, valueComparator);
}
}

Expand Down Expand Up @@ -438,6 +439,32 @@ static <K, V> ImmutableSetMultimap<K, V> fromMapEntries(
return new ImmutableSetMultimap<>(builder.buildOrThrow(), size, valueComparator);
}

/** Creates an ImmutableSetMultimap from a map to builders. */
static <K, V> ImmutableSetMultimap<K, V> fromMapBuilderEntries(
Collection<? extends Map.Entry<K, ImmutableCollection.Builder<V>>> mapEntries,
@CheckForNull Comparator<? super V> valueComparator) {
if (mapEntries.isEmpty()) {
return of();
}
ImmutableMap.Builder<K, ImmutableSet<V>> builder =
new ImmutableMap.Builder<>(mapEntries.size());
int size = 0;

for (Entry<K, ImmutableCollection.Builder<V>> entry : mapEntries) {
K key = entry.getKey();
ImmutableSet.Builder<? extends V> values = (ImmutableSet.Builder<V>) entry.getValue();
// If orderValuesBy got called at the very end, we may need to do the ImmutableSet to
// ImmutableSortedSet copy for each of these.
ImmutableSet<V> set = valueSet(valueComparator, values.build());
if (!set.isEmpty()) {
builder.put(key, set);
size += set.size();
}
}

return new ImmutableSetMultimap<>(builder.buildOrThrow(), size, valueComparator);
}

/**
* Returned by get() when a missing key is provided. Also holds the comparator, if any, used for
* values.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -353,5 +353,10 @@ Builder<E> combine(Builder<E> builder) {
public ImmutableList<E> build() {
return copyOf(contents);
}

ImmutableList<E> buildSorted(Comparator<? super E> comparator) {
Collections.sort(contents, comparator);
return copyOf(contents);
}
}
}
15 changes: 15 additions & 0 deletions guava/src/com/google/common/collect/ImmutableList.java
Original file line number Diff line number Diff line change
Expand Up @@ -926,6 +926,21 @@ public ImmutableList<E> build() {
forceCopy = true;
return asImmutableList(contents, size);
}

/**
* Returns a newly-created {@code ImmutableList} based on the contents of the {@code Builder},
* sorted according to the specified comparator.
*/
@SuppressWarnings("unchecked")
ImmutableList<E> buildSorted(Comparator<? super E> comparator) {
// Currently only used by ImmutableListMultimap.Builder.orderValuesBy.
// In particular, this implies that the comparator can never get "removed," so this can't
// invalidate future builds.

forceCopy = true;
Arrays.sort((E[]) contents, 0, size, comparator);
return asImmutableList(contents, size);
}
}

private static final long serialVersionUID = 0xcafebabe;
Expand Down
23 changes: 23 additions & 0 deletions guava/src/com/google/common/collect/ImmutableListMultimap.java
Original file line number Diff line number Diff line change
Expand Up @@ -379,6 +379,29 @@ static <K, V> ImmutableListMultimap<K, V> fromMapEntries(
return new ImmutableListMultimap<>(builder.buildOrThrow(), size);
}

/** Creates an ImmutableListMultimap from an asMap.entrySet. */
static <K, V> ImmutableListMultimap<K, V> fromMapBuilderEntries(
Collection<? extends Map.Entry<K, ImmutableCollection.Builder<V>>> mapEntries,
@CheckForNull Comparator<? super V> valueComparator) {
if (mapEntries.isEmpty()) {
return of();
}
ImmutableMap.Builder<K, ImmutableList<V>> builder =
new ImmutableMap.Builder<>(mapEntries.size());
int size = 0;

for (Entry<K, ImmutableCollection.Builder<V>> entry : mapEntries) {
K key = entry.getKey();
ImmutableList.Builder<V> values = (ImmutableList.Builder<V>) entry.getValue();
ImmutableList<V> list =
(valueComparator == null) ? values.build() : values.buildSorted(valueComparator);
builder.put(key, list);
size += list.size();
}

return new ImmutableListMultimap<>(builder.buildOrThrow(), size);
}

ImmutableListMultimap(ImmutableMap<K, ImmutableList<V>> map, int size) {
super(map, size);
}
Expand Down
Loading

0 comments on commit 040d0b9

Please sign in to comment.