Java

LinkedHashMap and Specialized Maps

Master LinkedHashMap for predictable iteration and LRU caching. Explore WeakHashMap for memory-sensitive caching and IdentityHashMap for reference-based equality. Build production-ready caches in under 20 lines of code.

📋 At a Glance

AspectDetails
TopicLinkedHashMap, WeakHashMap, IdentityHashMap, EnumMap
ComplexityIntermediate to Advanced
PrerequisitesPart 12 (HashMap Internals)
Time to Master3-4 hours
Interview FrequencyVery High (LRU cache implementation)

🎯 What You'll Learn

After completing this article, you will be able to:

  1. Implement LRU cache with LinkedHashMap in 20 lines
  2. Understand accessOrder vs insertionOrder in LinkedHashMap
  3. Use WeakHashMap for memory-sensitive caching
  4. Apply IdentityHashMap for reference equality scenarios
  5. Choose the right specialized map for each use case

Production Story: The 20-Line LRU Cache That Saved the Day

The Incident

Our image processing service was running out of memory. Each processed image was being cached indefinitely, and with thousands of requests per minute, we were hitting OOM errors.

JAVA(12 lines)
Code
Loading syntax highlighter...

The ops team was scaling up servers, but the real problem was unbounded cache growth.

The 20-Line Fix

JAVA(18 lines)
Code
Loading syntax highlighter...

How It Works

TEXT(15 lines)
Code
Loading syntax highlighter...

The removeEldestEntry Hook

JAVA(15 lines)
Code
Loading syntax highlighter...

Impact

  • Memory usage dropped from 8GB to 500MB
  • Response time improved (less GC pressure)
  • Zero additional dependencies
  • Solution fits in a single file

Mental Model: The Restaurant Waiting List

TEXT(30 lines)
Code
Loading syntax highlighter...

Deep Dive: LinkedHashMap Internals

Entry Structure

JAVA(10 lines)
Code
Loading syntax highlighter...

Visual: HashMap + Linked List

TEXT(20 lines)
Code
Loading syntax highlighter...

Construction Options

JAVA(19 lines)
Code
Loading syntax highlighter...

Access Order Behavior

JAVA(21 lines)
Code
Loading syntax highlighter...

Deep Dive: LRU Cache Implementation

Basic LRU Cache

JAVA(18 lines)
Code
Loading syntax highlighter...

LRU Cache with Expiration

JAVA(43 lines)
Code
Loading syntax highlighter...

LRU Cache with Load Function

JAVA(27 lines)
Code
Loading syntax highlighter...

Deep Dive: WeakHashMap

How WeakHashMap Works

JAVA(12 lines)
Code
Loading syntax highlighter...

WeakHashMap Internals

JAVA(18 lines)
Code
Loading syntax highlighter...

WeakHashMap Use Cases

JAVA(23 lines)
Code
Loading syntax highlighter...

WeakHashMap Gotchas

JAVA(18 lines)
Code
Loading syntax highlighter...

Deep Dive: IdentityHashMap

Reference Equality vs Object Equality

JAVA(21 lines)
Code
Loading syntax highlighter...

IdentityHashMap Use Cases

JAVA(23 lines)
Code
Loading syntax highlighter...

IdentityHashMap Internals

JAVA(9 lines)
Code
Loading syntax highlighter...

Deep Dive: EnumMap

Ultra-Fast Enum Key Maps

JAVA(16 lines)
Code
Loading syntax highlighter...

EnumMap Performance

JAVA(9 lines)
Code
Loading syntax highlighter...

Specialized Map Comparison

TEXT(13 lines)
Code
Loading syntax highlighter...

⚠️ Common Mistakes

Mistake 1: Forgetting accessOrder Parameter

JAVA(7 lines)
Code
Loading syntax highlighter...

Mistake 2: WeakHashMap with Strong Value References

JAVA(14 lines)
Code
Loading syntax highlighter...

Mistake 3: Using WeakHashMap for Value-Based Caching

JAVA(9 lines)
Code
Loading syntax highlighter...

Mistake 4: Modifying LinkedHashMap During Iteration

JAVA(19 lines)
Code
Loading syntax highlighter...

Mistake 5: IdentityHashMap with Immutable Keys

JAVA(11 lines)
Code
Loading syntax highlighter...

🐛 Debug This

Challenge 1: The Non-Evicting Cache

JAVA(14 lines)
Code
Loading syntax highlighter...
✅ Answer:
Output: [C, A, D]
  1. After puts: A, B, C (insertion order)
  2. get("A") moves A to end: B, C, A
  3. put("D") adds D, size=4 > 3, removes eldest (B): C, A, D

Challenge 2: The Immortal Entry

JAVA(8 lines)
Code
Loading syntax highlighter...
✅ Answer:
Output: 1
  • "key" is a string literal (interned), always reachable → stays
  • new String("key2") has no other references → collected by GC

Challenge 3: The Identity Puzzle

JAVA(7 lines)
Code
Loading syntax highlighter...
✅ Answer:
Output: 2

Integer caches values from -128 to 127. So:

  • 100 is cached → same object both times → 1 entry
  • 200 is NOT cached → different objects → 2 entries

Wait, that would be 3 entries. Let me reconsider...

Actually:

  • First 100 and second 100 are same cached object → 1 entry
  • First 200 and second 200 are different objects (no caching) → 2 entries

Total: 3 entries? Let me verify the Integer cache behavior...

Integer caches -128 to 127 by default. 100 is in range, so both 100 literals are the same object. 200 is outside the range, so 200 creates new Integer objects each time.
Output: 3 (one for 100, two for different 200 objects)

💻 Exercises

Exercise 1: Thread-Safe LRU Cache

Implement a thread-safe LRU cache:

JAVA(5 lines)
Code
Loading syntax highlighter...
✅ Solution:
JAVA(33 lines)
Code
Loading syntax highlighter...

Exercise 2: WeakValueHashMap

Implement a map where values (not keys) are weakly referenced:

JAVA(5 lines)
Code
Loading syntax highlighter...
✅ Solution:
JAVA(38 lines)
Code
Loading syntax highlighter...

Exercise 3: Most Recently Used (MRU) Cache

Implement a cache that evicts the MOST recently used item (opposite of LRU):

JAVA(4 lines)
Code
Loading syntax highlighter...
✅ Solution:
JAVA(32 lines)
Code
Loading syntax highlighter...

Exercise 4: Caching with Soft References

Implement a memory-sensitive cache using SoftReferences:

JAVA(5 lines)
Code
Loading syntax highlighter...
✅ Solution:
JAVA(38 lines)
Code
Loading syntax highlighter...

Exercise 5: Object Graph Size Calculator

Use IdentityHashMap to calculate unique object count in an object graph:

JAVA(4 lines)
Code
Loading syntax highlighter...
✅ Solution:
JAVA(42 lines)
Code
Loading syntax highlighter...

🎤 Senior-Level Interview Questions

Question 1: LRU Cache Implementation

Q: Implement an LRU cache with O(1) get and put operations.
A:
JAVA(18 lines)
Code
Loading syntax highlighter...

Question 2: LinkedHashMap Access Order

Q: What operations trigger access-order reordering in LinkedHashMap?
A:

Operations that trigger reordering (move to end):

  • get(key) when key exists
  • put(key, value) - both insert and update
  • putIfAbsent(key, value) when key doesn't exist
  • compute(), computeIfAbsent(), computeIfPresent()
  • merge()
  • replace()

Operations that DON'T trigger reordering:

  • containsKey(key) - read-only check
  • containsValue(value) - read-only check
  • Iteration - doesn't modify structure
  • remove(key) - removes, doesn't reorder

Question 3: WeakHashMap GC Behavior

Q: When are entries actually removed from WeakHashMap?
A:

Entries are removed lazily, not immediately after key is GC'd:

  1. Key becomes unreachable
  2. GC runs and collects the key
  3. WeakReference is added to ReferenceQueue
  4. Next WeakHashMap operation polls queue and removes stale entries
JAVA(9 lines)
Code
Loading syntax highlighter...
Cleanup happens during: get(), put(), size(), containsKey(), iteration, etc.

Question 4: IdentityHashMap Use Cases

Q: When would you use IdentityHashMap over HashMap?
A:

Use IdentityHashMap when:

  1. Object graph traversal: Track visited objects without equals() interference
  2. Serialization: Map objects to IDs by identity
  3. Proxy/wrapper tracking: Original object → proxy mapping
  4. Interned object handling: When you specifically want reference equality
JAVA(12 lines)
Code
Loading syntax highlighter...

Question 5: EnumMap Performance

Q: Why is EnumMap faster and more memory-efficient than HashMap for enum keys?
A:

EnumMap uses the enum's ordinal as array index:

JAVA(14 lines)
Code
Loading syntax highlighter...

Question 6: Thread Safety in LinkedHashMap

Q: How would you make an LRU cache thread-safe?
A:

Three approaches:

JAVA(24 lines)
Code
Loading syntax highlighter...

📝 Summary & Key Takeaways

LinkedHashMap

  • HashMap + doubly-linked list for ordering
  • insertionOrder (default) or accessOrder (for LRU)
  • removeEldestEntry() hook for cache eviction
  • O(1) operations, slight memory overhead for links

LRU Cache Pattern

JAVA(5 lines)
Code
Loading syntax highlighter...

WeakHashMap

  • Keys are WeakReferences, auto-collected by GC
  • Use for caches where key lifecycle controls entry lifecycle
  • Cleanup is lazy (on next operation)
  • Don't use with string literals or when value references key

IdentityHashMap

  • Uses == instead of equals()
  • Use for object graph traversal, serialization
  • More memory-efficient for small maps
  • Linear probing instead of chaining

EnumMap

  • O(1) array-indexed by ordinal
  • Always use for enum keys (no exceptions!)
  • Most memory-efficient map for enums

🏁 Conclusion

Specialized maps solve specific problems elegantly. LinkedHashMap enables LRU caches in 20 lines, WeakHashMap provides automatic cleanup tied to GC, and IdentityHashMap handles reference equality scenarios. Knowing when to use each saves hours of custom implementation.

Key takeaways:

  1. LRU cache = LinkedHashMap with accessOrder=true + removeEldestEntry
  2. WeakHashMap for key-lifecycle-based caching - not for value-based expiration
  3. IdentityHashMap for object identity - graph traversal, serialization
  4. EnumMap for enum keys - always, no exceptions
  5. Thread safety requires extra work - use Caffeine for production caches

In the next article, we'll explore PriorityQueue and heap-based data structures - essential for scheduling, task prioritization, and the classic "top K" problems.


📅 Review Schedule

To solidify your understanding, review this material:

  • Tomorrow: Implement an LRU cache from memory
  • In 3 days: Explain WeakHashMap GC behavior to a colleague
  • In 1 week: Review IdentityHashMap use cases
  • In 2 weeks: Compare all specialized maps from memory