Boost Java Performance: 5 Proven HashMap & Enum Optimizations
This article presents five practical Java performance tweaks—pre‑allocating HashMap capacity, using object keys, caching Enum values, replacing String constants with Enums, and upgrading the JDK—backed by JMH benchmarks that show up to 9.5× speed gains.
This article introduces several small yet effective performance‑optimization tricks for Java developers, emphasizing that premature optimization should be avoided.
Tip: Do not optimize for the sake of optimization; it can increase code complexity.
The benchmarks were run using JMH version 1.33 on JDK 17 (OpenJDK 64‑Bit Server VM, 17+35‑2724).
Pre‑allocate HashMap size, improving performance by about 25%.
Optimize HashMap keys by using object references instead of concatenated strings, achieving a 9.5× speedup.
Avoid using Enum.values() for iteration; cache the values array to reduce allocation overhead.
Replace String constants with Enums; using EnumMap outperforms HashMap by roughly 1.5×.
Use a newer JDK version; basic operations can be 2–5× faster.
Pre‑allocate HashMap Size
HashMap is a frequently used collection in Java. When its capacity needs to grow, the operation is slow, so it is advisable to set an appropriate initial size based on the load factor (default 0.75).
@State(Scope.Benchmark)
@Warmup(iterations = 3, time = 3)
@Measurement(iterations = 5, time = 3)
public class HashMapSize {
@Param({"14"})
int keys;
@Param({"16", "32"})
int size;
@Benchmark
public HashMap<Integer, Integer> getHashMap() {
HashMap<Integer, Integer> map = new HashMap<>(size);
for (int i = 0; i < keys; i++) {
map.put(i, i);
}
return map;
}
}With an initial capacity of 16, inserting 14 elements triggers a resize; with capacity 32, no resize occurs because 32 × 0.75 = 24 slots are available.
# JMH version: 1.33
# VM version: JDK 17, OpenJDK 64-Bit Server VM, 17+35-2724
Benchmark (keys) (size) Mode Cnt Score Error Units
HashMapSize.getHashMap 14 16 thrpt 25 4825825.152 ± 323910.557 ops/s
HashMapSize.getHashMap 14 32 thrpt 25 6556184.664 ± 711657.679 ops/sThe 32‑capacity HashMap processes about 26% more operations per second, confirming a roughly 1/4 performance improvement.
Optimize HashMap Key
When a HashMap key consists of multiple strings, encapsulating those strings in a custom object and using that object as the key is faster than concatenating strings.
@State(Scope.Benchmark)
@Warmup(iterations = 3, time = 3)
@Measurement(iterations = 5, time = 3)
public class HashMapKey {
private int size = 1024;
private Map<String, Object> stringMap;
private Map<Pair, Object> pairMap;
private String[] prefixes;
private String[] suffixes;
@Setup(Level.Trial)
public void setup() {
prefixes = new String[size];
suffixes = new String[size];
stringMap = new HashMap<>();
pairMap = new HashMap<>();
for (int i = 0; i < size; ++i) {
prefixes[i] = UUID.randomUUID().toString();
suffixes[i] = UUID.randomUUID().toString();
stringMap.put(prefixes[i] + ";" + suffixes[i], i);
pairMap.put(new MutablePair(prefixes[i], suffixes[i]), i);
}
}
@Benchmark
@OperationsPerInvocation(1024)
public void stringKey(Blackhole bh) {
for (int i = 0; i < prefixes.length; i++) {
bh.consume(stringMap.get(prefixes[i] + ";" + suffixes[i]));
}
}
@Benchmark
@OperationsPerInvocation(1024)
public void pairMap(Blackhole bh) {
for (int i = 0; i < prefixes.length; i++) {
bh.consume(pairMap.get(new MutablePair(prefixes[i], suffixes[i])));
}
}
} # JMH version: 1.33
# VM version: JDK 17, OpenJDK 64-Bit Server VM, 17+35-2724
Benchmark Mode Cnt Score Error Units
HashMapKey.pairMap thrpt 25 89295035.436 ± 6498403.173 ops/s
HashMapKey.stringKey thrpt 25 9410641.728 ± 389850.653 ops/sUsing an object reference as the key is about 9.5 times faster than using a concatenated string.
Avoid Enum.values() Iteration
Calling Enum.values() creates a new array each time. Caching the array eliminates this allocation.
@State(Scope.Benchmark)
@Warmup(iterations = 3, time = 3)
@Measurement(iterations = 5, time = 3)
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.MILLISECONDS)
public class EnumIteration {
enum FourteenEnum {
a,b,c,d,e,f,g,h,i,j,k,l,m,n;
static final FourteenEnum[] VALUES;
static { VALUES = values(); }
}
@Benchmark
public void valuesEnum(Blackhole bh) {
for (FourteenEnum v : FourteenEnum.values()) {
bh.consume(v.ordinal());
}
}
@Benchmark
public void enumSetEnum(Blackhole bh) {
for (FourteenEnum v : EnumSet.allOf(FourteenEnum.class)) {
bh.consume(v.ordinal());
}
}
@Benchmark
public void cacheEnums(Blackhole bh) {
for (FourteenEnum v : FourteenEnum.VALUES) {
bh.consume(v.ordinal());
}
}
} # JMH version: 1.33
# VM version: JDK 17, OpenJDK 64-Bit Server VM, 17+35-2724
Benchmark Mode Cnt Score Error Units
EnumIteration.cacheEnums thrpt 25 15623401.567 ± 2274962.772 ops/s
EnumIteration.enumSetEnum thrpt 25 8597188.662 ± 610632.249 ops/s
EnumIteration.valuesEnum thrpt 25 14713941.570 ± 728955.826 ops/sCaching the enum array yields the best throughput; EnumSet traversal is the slowest because it relies on a hash‑based structure.
Use Enum Instead of String Constants
Replacing String constants with Enums provides type safety and better performance. When used as map keys, EnumMap outperforms HashMap with String keys.
@State(Scope.Benchmark)
@Warmup(iterations = 3, time = 3)
@Measurement(iterations = 5, time = 3)
public class EnumMapBenchmark {
enum AnEnum { a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z; }
static final int size = 10000;
static final int seed = 99;
@State(Scope.Benchmark)
public static class EnumMapState {
EnumMap<AnEnum, String> map;
AnEnum[] values;
@Setup(Level.Trial)
public void setup() {
map = new EnumMap<>(AnEnum.class);
values = new AnEnum[size];
AnEnum[] enumValues = AnEnum.values();
SplittableRandom random = new SplittableRandom(seed);
for (int i = 0; i < size; i++) {
values[i] = enumValues[random.nextInt(0, enumValues.length)];
}
for (AnEnum v : enumValues) {
map.put(v, UUID.randomUUID().toString());
}
}
}
@State(Scope.Benchmark)
public static class HashMapState {
HashMap<String, String> map;
String[] values;
@Setup(Level.Trial)
public void setup() {
map = new HashMap<>();
values = new String[size];
AnEnum[] enumValues = AnEnum.values();
SplittableRandom random = new SplittableRandom(seed);
for (int i = 0; i < size; i++) {
values[i] = enumValues[random.nextInt(0, enumValues.length)].toString();
}
for (AnEnum v : enumValues) {
map.put(v.toString(), UUID.randomUUID().toString());
}
}
}
@Benchmark
public void enumMap(EnumMapState state, Blackhole bh) {
for (AnEnum v : state.values) {
bh.consume(state.map.get(v));
}
}
@Benchmark
public void hashMap(HashMapState state, Blackhole bh) {
for (String v : state.values) {
bh.consume(state.map.get(v));
}
}
} # JMH version: 1.33
# VM version: JDK 17, OpenJDK 64-Bit Server VM, 17+35-2724
Benchmark Mode Cnt Score Error Units
EnumMapBenchmark.enumMap thrpt 25 22159.232 ± 1268.800 ops/s
EnumMapBenchmark.hashMap thrpt 25 14528.555 ± 1323.610 ops/sUsing Enums as keys yields about 1.5× higher throughput than using Strings.
Use a Higher‑Version JDK
String handling has been optimized in newer JDK releases. Benchmarks compare Java 8 and Java 11/17 for converting between String and byte[].
@State(Scope.Benchmark)
@Warmup(iterations = 3, time = 3)
@Measurement(iterations = 5, time = 3)
public class StringInJdk {
@Param({"10000"})
private int size;
private String[] stringArray;
private List<byte[]> byteList;
@Setup(Level.Trial)
public void setup() {
byteList = new ArrayList<>(size);
stringArray = new String[size];
for (int i = 0; i < size; i++) {
String uuid = UUID.randomUUID().toString();
stringArray[i] = uuid;
byteList.add(uuid.getBytes(StandardCharsets.UTF_8));
}
}
@Benchmark
public void byteToString(Blackhole bh) {
for (byte[] b : byteList) {
bh.consume(new String(b, StandardCharsets.UTF_8));
}
}
@Benchmark
public void stringToByte(Blackhole bh) {
for (String s : stringArray) {
bh.consume(s.getBytes(StandardCharsets.UTF_8));
}
}
} # JMH version: 1.33
# VM version: JDK 1.8.0_151, Java HotSpot(TM) 64-Bit Server VM, 25.151-b12
Benchmark (size) Mode Cnt Score Error Units
StringInJdk.byteToString 10000 thrpt 25 2396.713 ± 133.500 ops/s
StringInJdk.stringToByte 10000 thrpt 25 1745.060 ± 16.945 ops/s
# JMH version: 1.33
# VM version: JDK 17, OpenJDK 64-Bit Server VM, 17+35-2724
Benchmark (size) Mode Cnt Score Error Units
StringInJdk.byteToString 10000 thrpt 25 5711.954 ± 41.865 ops/s
StringInJdk.stringToByte 10000 thrpt 25 8595.895 ± 704.004 ops/sOn Java 17, converting byte[] → String is about 2.5× faster and String → byte[] is roughly 5× faster than on Java 8, highlighting the benefit of upgrading the JDK.
Tip: Do not optimize without a real performance problem; unnecessary tweaks can add complexity.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Su San Talks Tech
Su San, former staff at several leading tech companies, is a top creator on Juejin and a premium creator on CSDN, and runs the free coding practice site www.susan.net.cn.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
