return ImmutableMap.of();
}
if (size == 1) {
- // Efficient single-entry implementation
+ // Efficient single-entry implementation.
final Entry<K, V> e = m.entrySet().iterator().next();
return SharedSingletonMap.of(e.getKey(), e.getValue());
}
- final Map<K, Integer> offsets = OffsetMapCache.offsetsFor(m.keySet());
+ // copyOf() disconnects the key set while retaining its order across cache lookup.
+ final Map<K, Integer> offsets = OffsetMapCache.orderedOffsets(m.keySet());
@SuppressWarnings("unchecked")
final V[] array = (V[]) new Object[offsets.size()];
for (Entry<K, V> e : m.entrySet()) {
@Override
public MutableOffsetMap<K, V> toModifiableMap() {
- return new MutableOffsetMap<>(this);
+ return MutableOffsetMap.copyOf(this);
}
@Override
private void readObject(final ObjectInputStream in) throws IOException, ClassNotFoundException {
final int s = in.readInt();
+ // ImmutableList.build() does not have a sizing hint ...
final List<K> keys = new ArrayList<>(s);
final V[] values = (V[]) new Object[s];
values[i] = (V)in.readObject();
}
- setField(OFFSETS_FIELD, OffsetMapCache.offsetsFor(keys));
+ setField(OFFSETS_FIELD, OffsetMapCache.orderedOffsets(keys));
setField(ARRAY_FIELD, values);
}
}
import com.google.common.annotations.Beta;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import java.util.AbstractMap;
import java.util.AbstractSet;
import java.util.ArrayList;
import java.util.Arrays;
-import java.util.Collection;
-import java.util.Collections;
import java.util.ConcurrentModificationException;
import java.util.Iterator;
import java.util.LinkedHashMap;
+import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Set;
private boolean needClone = true;
public MutableOffsetMap() {
- this(Collections.<K>emptySet());
+ this(ImmutableList.<K>of());
}
+ @VisibleForTesting
@SuppressWarnings("unchecked")
- protected MutableOffsetMap(final Collection<K> keySet) {
+ MutableOffsetMap(final List<K> keySet) {
if (!keySet.isEmpty()) {
removed = keySet.size();
- offsets = OffsetMapCache.offsetsFor(keySet);
+ offsets = OffsetMapCache.orderedOffsets(keySet);
objects = (V[])new Object[removed];
} else {
offsets = ImmutableMap.of();
this.newKeys = new LinkedHashMap<>();
}
- protected MutableOffsetMap(final ImmutableOffsetMap<K, V> m) {
- this(m.offsets(), m.objects());
- }
-
- @SuppressWarnings("unchecked")
- protected MutableOffsetMap(final Map<K, V> m) {
- this(OffsetMapCache.offsetsFor(m.keySet()), (V[])m.values().toArray());
- }
-
protected MutableOffsetMap(final MutableOffsetMap<K, V> m) {
this.offsets = m.offsets;
this.objects = m.objects;
return ((MutableOffsetMap<K, V>) m).clone();
}
if (m instanceof ImmutableOffsetMap) {
- return ((ImmutableOffsetMap<K, V>) m).toModifiableMap();
+ final ImmutableOffsetMap<K, V> map = (ImmutableOffsetMap<K, V>) m;
+ return new MutableOffsetMap<>(map.offsets(), map.objects());
}
- return new MutableOffsetMap<>(m);
+ @SuppressWarnings("unchecked")
+ final V[] values = (V[])m.values().toArray();
+ return new MutableOffsetMap<>(OffsetMapCache.orderedOffsets(m.keySet()), values);
}
public static <K, V> MutableOffsetMap<K, V> forOffsets(final Map<K, Integer> offsets) {
return new MutableOffsetMap<>(offsets, objects);
}
- public static <K, V> MutableOffsetMap<K, V> forKeySet(final Collection<K> keySet) {
- return forOffsets(OffsetMapCache.offsetsFor(keySet));
- }
-
@Override
public int size() {
return offsets.size() + newKeys.size() - removed;
}
// Construct the set of keys
- final Collection<K> keyset = new ArrayList<>(s);
+ final List<K> keyset = new ArrayList<>(s);
if (removed != 0) {
if (removed != offsets.size()) {
for (Entry<K, Integer> e : offsets.entrySet()) {
values[i++] = v;
}
- return new ImmutableOffsetMap<>(OffsetMapCache.offsetsFor(keyset), values);
+ return new ImmutableOffsetMap<>(OffsetMapCache.orderedOffsets(keyset), values);
}
@Override
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
+import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableMap.Builder;
+import com.google.common.collect.ImmutableSet;
import java.util.Collection;
import java.util.Map;
throw new UnsupportedOperationException();
}
+ /**
+ * Ordered lookup of offsets. Returned map will have the same iteration order.
+ */
@SuppressWarnings("unchecked")
- static <T> Map<T, Integer> offsetsFor(final Collection<T> args) {
- return (Map<T, Integer>) CACHE.getUnchecked(args);
+ static <T> Map<T, Integer> orderedOffsets(final Collection<T> args) {
+ return (Map<T, Integer>) CACHE.getUnchecked(ImmutableList.copyOf(args));
+ }
+
+ /**
+ * Unordered lookup of offsets. Returned map can have a different iterator order.
+ */
+ @SuppressWarnings("unchecked")
+ static <T> Map<T, Integer> unorderedOffsets(final Collection<T> args) {
+ return (Map<T, Integer>) CACHE.getUnchecked(ImmutableSet.copyOf(args));
}
}
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
+import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterators;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
@Test
public void testMutableWithKeyset() {
- final MutableOffsetMap<String, String> map = new MutableOffsetMap<>(ImmutableSet.of("k1", "k2"));
+ final MutableOffsetMap<String, String> map = new MutableOffsetMap<>(ImmutableList.of("k1", "k2"));
assertTrue(map.isEmpty());
assertTrue(map.keySet().isEmpty());
assertNull(map.get("k1"));