What is the quickest implementation of java.util.Map for a very small number of entries (under 15 elements or so)? Both thread-safe and non-thread-safe.
If all entries can be represented as Enums, use EnumMap:
This implementation combines the richness and safety of the Map interface with a speed approaching that of an array. If you want to map an enum to a value, you should always use an EnumMap in preference to an array.
If no, HashMap is good solution. It provides constant time for basic operations, like get()
and put()
:
This implementation provides constant-time performance for the basic operations (get and put), assuming the hash function disperses the elements properly among the buckets.
Just rembember to set low capacity
value in your HashMap:
Thus, it's very important not to set the initial capacity too high (or the load factor too low) if iteration performance is important.
Of course, above implementations are not thread safe. The best thread-safe implementation in this case will be ConcurrentHashMap. It combines high performance of HashMap with being thread-safe.
Here is good comparison of different Map implementations.
Edit: Here is an interesting comparison of different HashMap
implementations. It seems, that, at least for primitive types, there are faster alternatives than built-in HashMap
.
Edit2: Due to Peter Lawrey's answer I decided to perform some test, comparing ConcurrentHashMap
and Collections.synchronizedMap()
:
public static void main(String[] args) {
System.out.println("\n===== ConcurrentHashMap =====");
testMap(new ConcurrentHashMap<>());
System.out.println("\n===== Collections.synchronizedMap() =====");
testMap(Collections.synchronizedMap(new HashMap<>()));
}
static final int N = 5;
static final int R = 1000000;
public static void testMap(Map map) {
long startTime = System.nanoTime();
System.out.println("\n-- " + N*R + " puts(key, value) --");
startTime = System.nanoTime();
for (int j = 0; j < R; j++) {
for (int i = 0; i < N; i++)
map.put(i, new float[] { 0f, 1f, 2f, 3f, 4f });
map.clear();
}
System.out.println((System.nanoTime() - startTime) / 1000000000.0);
System.out.println("\n-- " + N*R + " get(key) --");
startTime = System.nanoTime();
for (int j = 0; j < R; j++) {
for (int i = 0; i < N; i++)
map.get(i);
map.clear();
}
System.out.println((System.nanoTime() - startTime) / 1000000000.0);
}
}
My results are:
===== ConcurrentHashMap =====
5000000 puts(key, value) - 0.99714195 s
5000000 get(key) - 0.452227427 s
===== Collections.synchronizedMap() =====
5000000 puts(key, value) - 0.586431367 s
5000000 get(key) - 0.376051088 s
So, Peter is probably right - for small maps, Collections.synchronizedMap()
is faster.
I'd say HashMaps.
HashMap<String, Object> map = new HashMap <String, Object> ();
will give you a very simple map with a key type of String and Value type of Object.
Edit: This is a non-thread safe version.
You can check out ConcurrentHashMaps for a thread-safe map.
If you want a compact Map you can use the
Map map = Collections.synchronizedMap(new HashMap<>());
if you don't need thread safety, drop the Collections.synchronizedMap
And if you want the collection to use as little memory as possible you can do something like this.
Map map = new HashMap<>(4, 1.0f);
This will store 4 key/values (or more) using a minimum of memory. It will be about half the size of an empty standard HashMap.
The problem with using ConcurrentHashMap is that it is heavy weight for a small collection i.e. it uses many objects and about 1 KB of memory if empty.
To get accurate memory usage, you need to use -XX-UseTLAB
on the command line
public static void main(String sdf[]) throws Exception {
testCreateSize("ConcurrentHashMap", ConcurrentHashMap::new);
testCreateSize("HashMap", HashMap::new);
testCreateSize("synchronized HashMap", () -> {
return Collections.synchronizedMap(new HashMap<>());
});
testCreateSize("small synchronized HashMap", () -> {
return Collections.synchronizedMap(new HashMap<>(4, 2.f));
});
}
public static void testCreateSize(String description, Supplier<Map<Integer, Integer>> supplier) {
// warmup.
supplier.get();
long start = memoryUsed();
Map<Integer, Integer> map = supplier.get();
for (int i = 0; i < 4; i++) {
map.put(i, i);
}
long used = memoryUsed() - start;
System.out.printf("Memory used for %s, was %,d bytes%n", description, used);
}
public static long memoryUsed() {
return Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory();
}
prints
Memory used for ConcurrentHashMap, was 272 bytes
Memory used for HashMap, was 256 bytes
Memory used for synchronized HashMap, was 288 bytes
Memory used for small synchronized HashMap, was 240 bytes
You can save 32 more bytes by using synchronized methods in the class which uses the map.