package org.opendaylight.yangtools.util;
import com.google.common.base.Preconditions;
-import com.google.common.collect.Iterables;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Maps;
import com.romix.scala.collection.concurrent.TrieMap;
-
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
-
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
DEFAULT_INSTANCE.persistMinItems, DEFAULT_INSTANCE.copyMaxItems);
}
- private static final int getProperty(final String name, final int defaultValue) {
+ private static int getProperty(final String name, final int defaultValue) {
try {
final String p = System.getProperty(name);
if (p != null) {
return DEFAULT_INSTANCE;
}
- public static MapAdaptor getInstance(final boolean useSingleton, final int copyMaxItems, final int persistMinItems) {
+ public static MapAdaptor getInstance(final boolean useSingleton, final int copyMaxItems,
+ final int persistMinItems) {
Preconditions.checkArgument(copyMaxItems >= 0, "copyMaxItems has to be a non-negative integer");
Preconditions.checkArgument(persistMinItems >= 0, "persistMinItems has to be a positive integer");
- Preconditions.checkArgument(persistMinItems <= copyMaxItems, "persistMinItems must be less than or equal to copyMaxItems");
+ Preconditions.checkArgument(persistMinItems <= copyMaxItems,
+ "persistMinItems must be less than or equal to copyMaxItems");
return new MapAdaptor(useSingleton, copyMaxItems, persistMinItems);
}
/**
- * Input is treated is supposed to be left unmodified, result must be mutable.
+ * Creates an initial snapshot. The backing map is selected according to
+ * the expected size.
*
- * @param input
- * @return
+ * @param expectedSize Expected map size
+ * @return An empty mutable map.
+ */
+ public <K, V> Map<K, V> initialSnapshot(final int expectedSize) {
+ Preconditions.checkArgument(expectedSize >= 0);
+ if (expectedSize > persistMinItems) {
+ return new ReadWriteTrieMap<>();
+ }
+
+ if (expectedSize < 2) {
+ return new HashMap<>(1);
+ }
+ if (expectedSize == 2) {
+ return new HashMap<>(2);
+ }
+ return Maps.newHashMapWithExpectedSize(expectedSize);
+ }
+
+ /**
+ * Input is treated is supposed to be left unmodified, result must be mutable.
*/
+ @SuppressWarnings("static-method")
public <K, V> Map<K, V> takeSnapshot(final Map<K, V> input) {
if (input instanceof ReadOnlyTrieMap) {
return ((ReadOnlyTrieMap<K, V>)input).toReadWrite();
LOG.trace("Converting input {} to a HashMap", input);
- // FIXME: be a bit smart about allocation based on observed size
+ /*
+ * The default HashMap copy constructor performs a bad thing for small maps, using the default capacity of 16
+ * as the minimum sizing hint, which can lead to wasted memory. Since the HashMap grows in powers-of-two, we
+ * only kick this in if we are storing 6 entries or less, as that results in 8-entry map -- the next power is
+ * 16, which is the default.
+ */
+ final Map<K, V> ret;
+ final int size = input.size();
+ if (size <= 6) {
+ final int target;
+ switch (size) {
+ case 0:
+ case 1:
+ target = 1;
+ break;
+ case 2:
+ target = 2;
+ break;
+ case 3:
+ target = 4;
+ break;
+ default:
+ target = 8;
+ }
+
+ ret = new HashMap<>(target);
+ ret.putAll(input);
+ } else if (input instanceof HashMap) {
+ // HashMap supports cloning, but we want to make sure we trim it down if entries were removed, so we do
+ // this only after having checked for small sizes.
+ @SuppressWarnings("unchecked")
+ final Map<K, V> tmp = (Map<K, V>) ((HashMap<K, V>) input).clone();
+ ret = tmp;
+ } else {
+ ret = new HashMap<>(input);
+ }
- final Map<K, V> ret = new HashMap<>(input);
LOG.trace("Read-write HashMap is {}", ret);
return ret;
}
*/
if (size == 0) {
LOG.trace("Reducing input {} to an empty map", input);
- return Collections.<K, V>emptyMap();
+ return ImmutableMap.of();
}
/*
* map.
*/
if (useSingleton && size == 1) {
- final Entry<K, V> e = Iterables.getOnlyElement(input.entrySet());
+ final Entry<K, V> e = input.entrySet().iterator().next();
final Map<K, V> ret = Collections.singletonMap(e.getKey(), e.getValue());
LOG.trace("Reducing input {} to singleton map {}", input, ret);
return ret;
* which will maintain the size for us.
*/
LOG.trace("Copying input {} to a TrieMap ({} entries)", input, size);
- final TrieMap<K, V> map = TrieMap.empty();
+ final TrieMap<K, V> map = new TrieMap<>();
map.putAll(input);
final Map<K, V> ret = new ReadOnlyTrieMap<>(map, size);
LOG.trace("Read-only TrieMap is {}", ret);