Skip to content

Commit cd3b419

Browse files
eamonnmcmanusGoogle Java Core Libraries
authored andcommitted
Add ImmutableMap.ofEntries.
Add to `ImmutableMap` a method like this: ``` @SafeVarargs public static <K, V> ImmutableMap<K, V> ofEntries(Map.Entry<? extends K, ? extends V>... entries) ``` The method throws IllegalArgumentException if two entries have the same key. It throws NullPointerExeption if `entries` is null or if any key or value in any entry is null. These specifications make it a drop-in replacement for [`Map.ofEntries`](https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/util/Map.html#ofEntries(java.util.Map.Entry...)) with the added advantages that it has an explicitly immutable type and that it has a defined iteration order (which is the order of the entries as they appear in the arguments to `ofEntries`). We hide `ofEntries` with a method in the subclass `ImmutableSortedMapFauxverrideShim` that has the same signature and throws an exception. We already do this for `ImmutableMap.of`, for example. If we did not do it then people could write `ImmutableSortedMap.ofEntries` and they would actually end up calling `ImmutableMap.ofEntries` since `ImmutableMap` is an ancestor of `ImmutableSortedMap`. We can't override `ofEntries` to return `ImmutableSortedMap` because we would want the `K` type parameter to have a `Comparable` bound, but you can't change type parameter bounds when overriding (really "hiding") static methods. People can achieve the desired effect with `ImmutableSortedMap.copyOf(ImmutableMap.ofEntries(...))`. (`java.util` has `Map.ofEntries` but it doesn't have `SortedMap.ofEntries` or `NavigableMap.ofEntries`.) We are not planning anything currently for `ImmutableMultimap` etc. RELNOTES=`collect`: Added `ImmutableMap.ofEntries`, like `Map.ofEntries` but for `ImmutableMap`. PiperOrigin-RevId: 395991240
1 parent c351aa4 commit cd3b419

File tree

14 files changed

+328
-50
lines changed

14 files changed

+328
-50
lines changed

android/guava-tests/test/com/google/common/collect/ImmutableBiMapTest.java

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
import com.google.common.collect.testing.google.BiMapInverseTester;
3333
import com.google.common.collect.testing.google.BiMapTestSuiteBuilder;
3434
import com.google.common.testing.SerializableTester;
35+
import java.util.AbstractMap;
3536
import java.util.Collections;
3637
import java.util.LinkedHashMap;
3738
import java.util.Map;
@@ -564,6 +565,30 @@ public void testOfWithDuplicateKey() {
564565
}
565566
}
566567

568+
public void testOfEntries() {
569+
assertMapEquals(
570+
ImmutableBiMap.ofEntries(entry("one", 1), entry("two", 2)), "one", 1, "two", 2);
571+
}
572+
573+
public void testOfEntriesNull() {
574+
Entry<Integer, Integer> nullKey = entry(null, 23);
575+
try {
576+
ImmutableBiMap.ofEntries(nullKey);
577+
fail();
578+
} catch (NullPointerException expected) {
579+
}
580+
Entry<Integer, Integer> nullValue = entry(23, null);
581+
try {
582+
ImmutableBiMap.ofEntries(nullValue);
583+
fail();
584+
} catch (NullPointerException expected) {
585+
}
586+
}
587+
588+
private static <T> Entry<T, T> entry(T key, T value) {
589+
return new AbstractMap.SimpleImmutableEntry<>(key, value);
590+
}
591+
567592
public void testCopyOfEmptyMap() {
568593
ImmutableBiMap<String, Integer> copy =
569594
ImmutableBiMap.copyOf(Collections.<String, Integer>emptyMap());

android/guava-tests/test/com/google/common/collect/ImmutableMapTest.java

Lines changed: 80 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818

1919
import static com.google.common.testing.SerializableTester.reserialize;
2020
import static com.google.common.truth.Truth.assertThat;
21+
import static com.google.common.truth.Truth.assertWithMessage;
2122

2223
import com.google.common.annotations.GwtCompatible;
2324
import com.google.common.annotations.GwtIncompatible;
@@ -49,6 +50,7 @@
4950
import java.io.ByteArrayOutputStream;
5051
import java.io.ObjectOutputStream;
5152
import java.io.Serializable;
53+
import java.util.AbstractMap;
5254
import java.util.Collection;
5355
import java.util.Collections;
5456
import java.util.LinkedHashMap;
@@ -979,15 +981,84 @@ public void ignore_testSerializationNoDuplication_regularImmutableMap() throws E
979981

980982
public void testEquals() {
981983
new EqualsTester()
982-
.addEqualityGroup(ImmutableMap.of(), ImmutableMap.builder().build())
983-
.addEqualityGroup(ImmutableMap.of(1, 1), ImmutableMap.builder().put(1, 1).build())
984-
.addEqualityGroup(ImmutableMap.of(1, 1, 2, 2))
985-
.addEqualityGroup(ImmutableMap.of(1, 1, 2, 2, 3, 3))
986-
.addEqualityGroup(ImmutableMap.of(1, 4, 2, 2, 3, 3))
987-
.addEqualityGroup(ImmutableMap.of(1, 1, 2, 4, 3, 3))
988-
.addEqualityGroup(ImmutableMap.of(1, 1, 2, 2, 3, 4))
989-
.addEqualityGroup(ImmutableMap.of(1, 2, 2, 3, 3, 1))
990-
.addEqualityGroup(ImmutableMap.of(1, 1, 2, 2, 3, 3, 4, 4))
984+
.addEqualityGroup(
985+
ImmutableMap.of(), ImmutableMap.builder().build(), ImmutableMap.ofEntries(), map())
986+
.addEqualityGroup(
987+
ImmutableMap.of(1, 1),
988+
ImmutableMap.builder().put(1, 1).build(),
989+
ImmutableMap.ofEntries(entry(1, 1)),
990+
map(1, 1))
991+
.addEqualityGroup(
992+
ImmutableMap.of(1, 1, 2, 2),
993+
ImmutableMap.builder().put(1, 1).put(2, 2).build(),
994+
ImmutableMap.ofEntries(entry(1, 1), entry(2, 2)),
995+
map(1, 1, 2, 2))
996+
.addEqualityGroup(
997+
ImmutableMap.of(1, 1, 2, 2, 3, 3),
998+
ImmutableMap.builder().put(1, 1).put(2, 2).put(3, 3).build(),
999+
ImmutableMap.ofEntries(entry(1, 1), entry(2, 2), entry(3, 3)),
1000+
map(1, 1, 2, 2, 3, 3))
1001+
.addEqualityGroup(
1002+
ImmutableMap.of(1, 4, 2, 2, 3, 3),
1003+
ImmutableMap.builder().put(1, 4).put(2, 2).put(3, 3).build(),
1004+
ImmutableMap.ofEntries(entry(1, 4), entry(2, 2), entry(3, 3)),
1005+
map(1, 4, 2, 2, 3, 3))
1006+
.addEqualityGroup(
1007+
ImmutableMap.of(1, 1, 2, 4, 3, 3),
1008+
ImmutableMap.builder().put(1, 1).put(2, 4).put(3, 3).build(),
1009+
ImmutableMap.ofEntries(entry(1, 1), entry(2, 4), entry(3, 3)),
1010+
map(1, 1, 2, 4, 3, 3))
1011+
.addEqualityGroup(
1012+
ImmutableMap.of(1, 1, 2, 2, 3, 4),
1013+
ImmutableMap.builder().put(1, 1).put(2, 2).put(3, 4).build(),
1014+
ImmutableMap.ofEntries(entry(1, 1), entry(2, 2), entry(3, 4)),
1015+
map(1, 1, 2, 2, 3, 4))
1016+
.addEqualityGroup(
1017+
ImmutableMap.of(1, 2, 2, 3, 3, 1),
1018+
ImmutableMap.builder().put(1, 2).put(2, 3).put(3, 1).build(),
1019+
ImmutableMap.ofEntries(entry(1, 2), entry(2, 3), entry(3, 1)),
1020+
map(1, 2, 2, 3, 3, 1))
1021+
.addEqualityGroup(
1022+
ImmutableMap.of(1, 1, 2, 2, 3, 3, 4, 4),
1023+
ImmutableMap.builder().put(1, 1).put(2, 2).put(3, 3).put(4, 4).build(),
1024+
ImmutableMap.ofEntries(entry(1, 1), entry(2, 2), entry(3, 3), entry(4, 4)),
1025+
map(1, 1, 2, 2, 3, 3, 4, 4))
1026+
.addEqualityGroup(
1027+
ImmutableMap.of(1, 1, 2, 2, 3, 3, 4, 4, 5, 5),
1028+
ImmutableMap.builder().put(1, 1).put(2, 2).put(3, 3).put(4, 4).put(5, 5).build(),
1029+
ImmutableMap.ofEntries(entry(1, 1), entry(2, 2), entry(3, 3), entry(4, 4), entry(5, 5)),
1030+
map(1, 1, 2, 2, 3, 3, 4, 4, 5, 5))
9911031
.testEquals();
9921032
}
1033+
1034+
public void testOfEntriesNull() {
1035+
Entry<Integer, Integer> nullKey = entry(null, 23);
1036+
try {
1037+
ImmutableMap.ofEntries(nullKey);
1038+
fail();
1039+
} catch (NullPointerException expected) {
1040+
}
1041+
Entry<Integer, Integer> nullValue = entry(23, null);
1042+
try {
1043+
ImmutableMap.ofEntries(nullValue);
1044+
fail();
1045+
} catch (NullPointerException expected) {
1046+
}
1047+
}
1048+
1049+
private static <T> Map<T, T> map(T... keysAndValues) {
1050+
assertThat(keysAndValues.length % 2).isEqualTo(0);
1051+
LinkedHashMap<T, T> map = new LinkedHashMap<>();
1052+
for (int i = 0; i < keysAndValues.length; i += 2) {
1053+
T key = keysAndValues[i];
1054+
T value = keysAndValues[i + 1];
1055+
T old = map.put(key, value);
1056+
assertWithMessage("Key %s set to %s and %s", key, value, old).that(old).isNull();
1057+
}
1058+
return map;
1059+
}
1060+
1061+
private static <T> Entry<T, T> entry(T key, T value) {
1062+
return new AbstractMap.SimpleImmutableEntry<>(key, value);
1063+
}
9931064
}

android/guava/src/com/google/common/collect/ImmutableBiMap.java

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import com.google.common.annotations.GwtCompatible;
2424
import com.google.errorprone.annotations.CanIgnoreReturnValue;
2525
import com.google.errorprone.annotations.DoNotCall;
26+
import java.util.Arrays;
2627
import java.util.Collection;
2728
import java.util.Comparator;
2829
import java.util.Map;
@@ -258,7 +259,20 @@ public static <K, V> ImmutableBiMap<K, V> of(
258259
10);
259260
}
260261

261-
// looking for of() with > 10 entries? Use the builder instead.
262+
// looking for of() with > 10 entries? Use the builder or ofEntries instead.
263+
264+
/**
265+
* Returns an immutable map containing the given entries, in order.
266+
*
267+
* @throws IllegalArgumentException if duplicate keys or values are provided
268+
* @since NEXT
269+
*/
270+
@SafeVarargs
271+
public static <K, V> ImmutableBiMap<K, V> ofEntries(Entry<? extends K, ? extends V>... entries) {
272+
@SuppressWarnings("unchecked") // we will only ever read these
273+
Entry<K, V>[] entries2 = (Entry<K, V>[]) entries;
274+
return copyOf(Arrays.asList(entries2));
275+
}
262276

263277
/**
264278
* Returns a new builder. The generated builder is equivalent to the builder created by the {@link

android/guava/src/com/google/common/collect/ImmutableMap.java

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -283,7 +283,20 @@ public static <K, V> ImmutableMap<K, V> of(
283283
});
284284
}
285285

286-
// looking for of() with > 10 entries? Use the builder instead.
286+
// looking for of() with > 10 entries? Use the builder or ofEntries instead.
287+
288+
/**
289+
* Returns an immutable map containing the given entries, in order.
290+
*
291+
* @throws IllegalArgumentException if duplicate keys are provided
292+
* @since NEXT
293+
*/
294+
@SafeVarargs
295+
public static <K, V> ImmutableMap<K, V> ofEntries(Entry<? extends K, ? extends V>... entries) {
296+
@SuppressWarnings("unchecked") // we will only ever read these
297+
Entry<K, V>[] entries2 = (Entry<K, V>[]) entries;
298+
return copyOf(Arrays.asList(entries2));
299+
}
287300

288301
/**
289302
* Verifies that {@code key} and {@code value} are non-null, and returns a new immutable entry

android/guava/src/com/google/common/collect/ImmutableSortedMap.java

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ private static <K, V> ImmutableSortedMap<K, V> of(Comparator<? super K> comparat
110110
@SuppressWarnings("unchecked")
111111
public static <K extends Comparable<? super K>, V> ImmutableSortedMap<K, V> of(
112112
K k1, V v1, K k2, V v2) {
113-
return ofEntries(entryOf(k1, v1), entryOf(k2, v2));
113+
return fromEntries(entryOf(k1, v1), entryOf(k2, v2));
114114
}
115115

116116
/**
@@ -122,7 +122,7 @@ public static <K extends Comparable<? super K>, V> ImmutableSortedMap<K, V> of(
122122
@SuppressWarnings("unchecked")
123123
public static <K extends Comparable<? super K>, V> ImmutableSortedMap<K, V> of(
124124
K k1, V v1, K k2, V v2, K k3, V v3) {
125-
return ofEntries(entryOf(k1, v1), entryOf(k2, v2), entryOf(k3, v3));
125+
return fromEntries(entryOf(k1, v1), entryOf(k2, v2), entryOf(k3, v3));
126126
}
127127

128128
/**
@@ -134,7 +134,7 @@ public static <K extends Comparable<? super K>, V> ImmutableSortedMap<K, V> of(
134134
@SuppressWarnings("unchecked")
135135
public static <K extends Comparable<? super K>, V> ImmutableSortedMap<K, V> of(
136136
K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4) {
137-
return ofEntries(entryOf(k1, v1), entryOf(k2, v2), entryOf(k3, v3), entryOf(k4, v4));
137+
return fromEntries(entryOf(k1, v1), entryOf(k2, v2), entryOf(k3, v3), entryOf(k4, v4));
138138
}
139139

140140
/**
@@ -146,7 +146,7 @@ public static <K extends Comparable<? super K>, V> ImmutableSortedMap<K, V> of(
146146
@SuppressWarnings("unchecked")
147147
public static <K extends Comparable<? super K>, V> ImmutableSortedMap<K, V> of(
148148
K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4, K k5, V v5) {
149-
return ofEntries(
149+
return fromEntries(
150150
entryOf(k1, v1), entryOf(k2, v2), entryOf(k3, v3), entryOf(k4, v4), entryOf(k5, v5));
151151
}
152152

@@ -160,7 +160,7 @@ public static <K extends Comparable<? super K>, V> ImmutableSortedMap<K, V> of(
160160
@SuppressWarnings("unchecked")
161161
public static <K extends Comparable<? super K>, V> ImmutableSortedMap<K, V> of(
162162
K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4, K k5, V v5, K k6, V v6) {
163-
return ofEntries(
163+
return fromEntries(
164164
entryOf(k1, v1),
165165
entryOf(k2, v2),
166166
entryOf(k3, v3),
@@ -179,7 +179,7 @@ public static <K extends Comparable<? super K>, V> ImmutableSortedMap<K, V> of(
179179
@SuppressWarnings("unchecked")
180180
public static <K extends Comparable<? super K>, V> ImmutableSortedMap<K, V> of(
181181
K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4, K k5, V v5, K k6, V v6, K k7, V v7) {
182-
return ofEntries(
182+
return fromEntries(
183183
entryOf(k1, v1),
184184
entryOf(k2, v2),
185185
entryOf(k3, v3),
@@ -214,7 +214,7 @@ public static <K extends Comparable<? super K>, V> ImmutableSortedMap<K, V> of(
214214
V v7,
215215
K k8,
216216
V v8) {
217-
return ofEntries(
217+
return fromEntries(
218218
entryOf(k1, v1),
219219
entryOf(k2, v2),
220220
entryOf(k3, v3),
@@ -252,7 +252,7 @@ public static <K extends Comparable<? super K>, V> ImmutableSortedMap<K, V> of(
252252
V v8,
253253
K k9,
254254
V v9) {
255-
return ofEntries(
255+
return fromEntries(
256256
entryOf(k1, v1),
257257
entryOf(k2, v2),
258258
entryOf(k3, v3),
@@ -293,7 +293,7 @@ public static <K extends Comparable<? super K>, V> ImmutableSortedMap<K, V> of(
293293
V v9,
294294
K k10,
295295
V v10) {
296-
return ofEntries(
296+
return fromEntries(
297297
entryOf(k1, v1),
298298
entryOf(k2, v2),
299299
entryOf(k3, v3),
@@ -306,11 +306,6 @@ public static <K extends Comparable<? super K>, V> ImmutableSortedMap<K, V> of(
306306
entryOf(k10, v10));
307307
}
308308

309-
private static <K extends Comparable<? super K>, V> ImmutableSortedMap<K, V> ofEntries(
310-
Entry<K, V>... entries) {
311-
return fromEntries(Ordering.natural(), false, entries, entries.length);
312-
}
313-
314309
/**
315310
* Returns an immutable map containing the same entries as {@code map}, sorted by the natural
316311
* ordering of the keys.
@@ -438,6 +433,11 @@ private static <K, V> ImmutableSortedMap<K, V> copyOfInternal(
438433
return fromEntries(comparator, sameComparator, map.entrySet());
439434
}
440435

436+
private static <K extends Comparable<? super K>, V> ImmutableSortedMap<K, V> fromEntries(
437+
Entry<K, V>... entries) {
438+
return fromEntries(Ordering.natural(), false, entries, entries.length);
439+
}
440+
441441
/**
442442
* Accepts a collection of possibly-null entries. If {@code sameComparator}, then it is assumed
443443
* that they do not need to be sorted or checked for dupes.

android/guava/src/com/google/common/collect/ImmutableSortedMapFauxverideShim.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -269,5 +269,17 @@ public static <K, V> ImmutableSortedMap<K, V> of(
269269
throw new UnsupportedOperationException();
270270
}
271271

272+
/**
273+
* Not supported. Use {@code ImmutableSortedMap.copyOf(ImmutableMap.ofEntries(...))}.
274+
*
275+
* @deprecated Use {@code ImmutableSortedMap.copyOf(ImmutableMap.ofEntries(...))}.
276+
*/
277+
@DoNotCall("ImmutableSortedMap.ofEntries not currently available; use ImmutableSortedMap.copyOf")
278+
@Deprecated
279+
public static <K, V> ImmutableSortedMap<K, V> ofEntries(
280+
Entry<? extends K, ? extends V>... entries) {
281+
throw new UnsupportedOperationException();
282+
}
283+
272284
// No copyOf() fauxveride; see ImmutableSortedSetFauxverideShim.
273285
}

guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/ImmutableBiMap.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,11 @@ public static <K, V> ImmutableBiMap<K, V> of(
147147
k1, v1, k2, v2, k3, v3, k4, v4, k5, v5, k6, v6, k7, v7, k8, v8, k9, v9, k10, v10));
148148
}
149149

150+
@SafeVarargs
151+
public static <K, V> ImmutableBiMap<K, V> ofEntries(Entry<? extends K, ? extends V>... entries) {
152+
return new RegularImmutableBiMap<K, V>(ImmutableMap.ofEntries(entries));
153+
}
154+
150155
public static <K, V> Builder<K, V> builder() {
151156
return new Builder<K, V>();
152157
}

guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/ImmutableMap.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -235,6 +235,11 @@ public static <K, V> ImmutableMap<K, V> of(
235235

236236
// looking for of() with > 10 entries? Use the builder instead.
237237

238+
@SafeVarargs
239+
public static <K, V> ImmutableMap<K, V> ofEntries(Entry<? extends K, ? extends V>... entries) {
240+
return new RegularImmutableMap(entries);
241+
}
242+
238243
public static <K, V> Builder<K, V> builder() {
239244
return new Builder<K, V>();
240245
}

guava-tests/test/com/google/common/collect/ImmutableBiMapTest.java

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
import com.google.common.testing.CollectorTester;
3838
import com.google.common.testing.SerializableTester;
3939
import com.google.errorprone.annotations.CanIgnoreReturnValue;
40+
import java.util.AbstractMap;
4041
import java.util.Arrays;
4142
import java.util.Collections;
4243
import java.util.EnumSet;
@@ -595,6 +596,30 @@ public void testOfWithDuplicateKey() {
595596
}
596597
}
597598

599+
public void testOfEntries() {
600+
assertMapEquals(
601+
ImmutableBiMap.ofEntries(entry("one", 1), entry("two", 2)), "one", 1, "two", 2);
602+
}
603+
604+
public void testOfEntriesNull() {
605+
Entry<Integer, Integer> nullKey = entry(null, 23);
606+
try {
607+
ImmutableBiMap.ofEntries(nullKey);
608+
fail();
609+
} catch (NullPointerException expected) {
610+
}
611+
Entry<Integer, Integer> nullValue = entry(23, null);
612+
try {
613+
ImmutableBiMap.ofEntries(nullValue);
614+
fail();
615+
} catch (NullPointerException expected) {
616+
}
617+
}
618+
619+
private static <T> Entry<T, T> entry(T key, T value) {
620+
return new AbstractMap.SimpleImmutableEntry<>(key, value);
621+
}
622+
598623
public void testCopyOfEmptyMap() {
599624
ImmutableBiMap<String, Integer> copy =
600625
ImmutableBiMap.copyOf(Collections.<String, Integer>emptyMap());

0 commit comments

Comments
 (0)