aboutsummaryrefslogtreecommitdiff
path: root/guava/src/com/google/common/collect/ImmutableMap.java
diff options
context:
space:
mode:
Diffstat (limited to 'guava/src/com/google/common/collect/ImmutableMap.java')
-rw-r--r--guava/src/com/google/common/collect/ImmutableMap.java91
1 files changed, 70 insertions, 21 deletions
diff --git a/guava/src/com/google/common/collect/ImmutableMap.java b/guava/src/com/google/common/collect/ImmutableMap.java
index 0a376ed9c..5c24df256 100644
--- a/guava/src/com/google/common/collect/ImmutableMap.java
+++ b/guava/src/com/google/common/collect/ImmutableMap.java
@@ -25,6 +25,7 @@ import com.google.common.annotations.Beta;
import com.google.common.annotations.GwtCompatible;
import com.google.common.annotations.VisibleForTesting;
import com.google.errorprone.annotations.CanIgnoreReturnValue;
+import com.google.errorprone.annotations.DoNotMock;
import com.google.errorprone.annotations.concurrent.LazyInit;
import com.google.j2objc.annotations.RetainedWith;
import com.google.j2objc.annotations.WeakOuter;
@@ -46,7 +47,6 @@ import java.util.function.BinaryOperator;
import java.util.function.Function;
import java.util.stream.Collector;
import java.util.stream.Collectors;
-import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
/**
@@ -60,6 +60,7 @@ import org.checkerframework.checker.nullness.qual.Nullable;
* @author Kevin Bourrillion
* @since 2.0
*/
+@DoNotMock("Use ImmutableMap.of or another implementation")
@GwtCompatible(serializable = true, emulated = true)
@SuppressWarnings("serial") // we're overriding default serialization
public abstract class ImmutableMap<K, V> implements Map<K, V>, Serializable {
@@ -244,8 +245,9 @@ public abstract class ImmutableMap<K, V> implements Map<K, V>, Serializable {
*
* @since 2.0
*/
+ @DoNotMock
public static class Builder<K, V> {
- @MonotonicNonNull Comparator<? super V> valueComparator;
+ @Nullable Comparator<? super V> valueComparator;
Entry<K, V>[] entries;
int size;
boolean entriesUsed;
@@ -492,7 +494,6 @@ public abstract class ImmutableMap<K, V> implements Map<K, V>, Serializable {
@Override
ImmutableSet<Entry<K, V>> createEntrySet() {
- @WeakOuter
class EntrySetImpl extends ImmutableMapEntrySet<K, V> {
@Override
ImmutableMap<K, V> map() {
@@ -705,7 +706,7 @@ public abstract class ImmutableMap<K, V> implements Map<K, V>, Serializable {
return (result != null) ? result : defaultValue;
}
- @LazyInit private transient ImmutableSet<Entry<K, V>> entrySet;
+ @LazyInit @RetainedWith private transient ImmutableSet<Entry<K, V>> entrySet;
/**
* Returns an immutable set of the mappings in this map. The iteration order is specified by the
@@ -890,37 +891,85 @@ public abstract class ImmutableMap<K, V> implements Map<K, V>, Serializable {
* reconstructed using public factory methods. This ensures that the implementation types remain
* as implementation details.
*/
- static class SerializedForm implements Serializable {
- private final Object[] keys;
- private final Object[] values;
-
- SerializedForm(ImmutableMap<?, ?> map) {
- keys = new Object[map.size()];
- values = new Object[map.size()];
- int i = 0;
- for (Entry<?, ?> entry : map.entrySet()) {
- keys[i] = entry.getKey();
- values[i] = entry.getValue();
- i++;
+ static class SerializedForm<K, V> implements Serializable {
+ // This object retains references to collections returned by keySet() and value(). This saves
+ // bytes when the both the map and its keySet or value collection are written to the same
+ // instance of ObjectOutputStream.
+
+ // TODO(b/160980469): remove support for the old serialization format after some time
+ private static final boolean USE_LEGACY_SERIALIZATION = true;
+
+ private final Object keys;
+ private final Object values;
+
+ SerializedForm(ImmutableMap<K, V> map) {
+ if (USE_LEGACY_SERIALIZATION) {
+ Object[] keys = new Object[map.size()];
+ Object[] values = new Object[map.size()];
+ int i = 0;
+ for (Entry<?, ?> entry : map.entrySet()) {
+ keys[i] = entry.getKey();
+ values[i] = entry.getValue();
+ i++;
+ }
+ this.keys = keys;
+ this.values = values;
+ return;
}
+ this.keys = map.keySet();
+ this.values = map.values();
}
- Object readResolve() {
- Builder<Object, Object> builder = new Builder<>(keys.length);
- return createMap(builder);
+ @SuppressWarnings("unchecked")
+ final Object readResolve() {
+ if (!(this.keys instanceof ImmutableSet)) {
+ return legacyReadResolve();
+ }
+
+ ImmutableSet<K> keySet = (ImmutableSet<K>) this.keys;
+ ImmutableCollection<V> values = (ImmutableCollection<V>) this.values;
+
+ Builder<K, V> builder = makeBuilder(keySet.size());
+
+ UnmodifiableIterator<K> keyIter = keySet.iterator();
+ UnmodifiableIterator<V> valueIter = values.iterator();
+
+ while (keyIter.hasNext()) {
+ builder.put(keyIter.next(), valueIter.next());
+ }
+
+ return builder.build();
}
- Object createMap(Builder<Object, Object> builder) {
+ @SuppressWarnings("unchecked")
+ final Object legacyReadResolve() {
+ K[] keys = (K[]) this.keys;
+ V[] values = (V[]) this.values;
+
+ Builder<K, V> builder = makeBuilder(keys.length);
+
for (int i = 0; i < keys.length; i++) {
builder.put(keys[i], values[i]);
}
return builder.build();
}
+ /**
+ * Returns a builder that builds the unserialized type. Subclasses should override this method.
+ */
+ Builder<K, V> makeBuilder(int size) {
+ return new Builder<>(size);
+ }
+
private static final long serialVersionUID = 0;
}
+ /**
+ * Returns a serializable form of this object. Non-public subclasses should not override this
+ * method. Publicly-accessible subclasses must override this method and should return a subclass
+ * of SerializedForm whose readResolve() method returns objects of the subclass type.
+ */
Object writeReplace() {
- return new SerializedForm(this);
+ return new SerializedForm<>(this);
}
}