When building Java apps that scale, efficient memory usage and performance become critical. Today, we’ll look at two powerful (but often underused) tools:
- 🔓 WeakHashMap / WeakHashSet – let the GC clean up what you don’t need
- 🧮 Predefining collection sizes – minimize resizing overhead
Let’s break these down with examples.
🔁 WeakHashMap and WeakHashSet: Smart Memory-Friendly Containers
🔑 WeakHashMap
in Action
import java.util.Map;
import java.util.WeakHashMap;
public class WeakMapExample {
public static void main(String[] args) {
Map<Object, String> map = new WeakHashMap<>();
Object key = new Object();
map.put(key, "Some value");
System.out.println("Before GC: " + map.size()); // 1
key = null; // Remove strong reference
System.gc(); // Hint the GC to collect
try { Thread.sleep(100); } catch (InterruptedException ignored) {}
System.out.println("After GC: " + map.size()); // Likely 0
}
}
💡 The key is garbage collected because the map only holds a weak reference to it.
🧺 Creating a WeakHashSet
import java.util.*;
public class WeakSetExample {
public static void main(String[] args) {
Set<Object> weakSet = Collections.newSetFromMap(new WeakHashMap<>());
Object item = new Object();
weakSet.add(item);
System.out.println("Before GC: " + weakSet.size()); // 1
item = null;
System.gc();
try { Thread.sleep(100); } catch (InterruptedException ignored) {}
System.out.println("After GC: " + weakSet.size()); // Likely 0
}
}
📌 Use case: A cache of objects you don’t want to prevent from being collected.
⚙️ Predefining Collection Sizes for Performance
When you know (or can estimate) the number of elements, setting an initial capacity saves time and memory.
🆚 Default vs Pre-Sized ArrayList
List<String> dynamicList = new ArrayList<>();
List<String> preSizedList = new ArrayList<>(1000);
for (int i = 0; i < 1000; i++) {
dynamicList.add("Item " + i);
preSizedList.add("Item " + i);
}
✅ The preSizedList
avoids multiple internal array resizes as it grows, leading to:
- Less heap churn
- Fewer GC cycles
- More predictable memory use
Predefining HashMap
Size
Map<Integer, String> map = new HashMap<>(1000);
for (int i = 0; i < 1000; i++) {
map.put(i, "Value " + i);
}
🔍 Internally, every resize in HashMap
involves rehashing all entries, which is expensive.
🧪 Benchmark It (Optional but Smart)
If you want to confirm performance gains in your context, try using JMH (Java Microbenchmark Harness). It’s a great way to test memory usage and execution time differences.
🧵 Recap
Concept | Use It For | Benefits | Watch Out |
---|---|---|---|
WeakHashMap |
Caches, listeners, soft associations | Auto-cleans unused keys | Don’t use for core state |
WeakHashSet |
Temporary or optional references | Prevents memory leaks | Created via Collections.newSetFromMap()
|
Predefined Sizes | Known/estimated data volume | Faster, fewer allocations | Overestimating wastes memory |
💬 Your Turn
Have you used weak references or optimized collection sizing in your projects? What kind of impact did you see? Share in the comments 👇