paint-brush
Changing Keys, Losing Valuesby@mcsee
570 reads
570 reads

Changing Keys, Losing Values

by Maximiliano Contieri4mFebruary 24th, 2025
Read on Terminal Reader
Read this story w/o Javascript

Too Long; Didn't Read

When you use mutable objects as keys in hashed collections, changing them breaks contracts.

Companies Mentioned

Mention Thumbnail
Mention Thumbnail
featured image - Changing Keys, Losing Values
Maximiliano Contieri HackerNoon profile picture
0-item


Changing Keys, Losing Values


TL;DR: When you use mutable objects as keys in hashed collections, changing them breaks contracts.

Problems 😔

  • Lost Values
  • Hard Debugging
  • The Least Surprise Principle Violation
  • Unexpected Behavior

Solutions 😃

  1. Use immutable objects as keys.
  2. Override equals/hashCode well.
  3. Use final fields (If your language allows it)
  4. Rehash after mutation (This is an over-engineering solution)

Context 💬

When you use mutable objects as keys in hashed collections, changing them key after after you add a related objec can make it unretrievable.


This happens because the hash code changes and the collection can't find the object in the correct bucket.

Sample Code 📖

Wrong ❌

class MutableKey {
    int id;

    MutableKey(int newId) {
        this.id = newId;
    }

    @Override
    public int hashCode() {
        return this.id;
    }

    @Override
    public boolean equals(Object objectToCompare) {
        if (this == objectToCompare) return true;
        MutableKey that = (MutableKey) objectToCompare;
        return id == that.id;
    }
}

MutableKey key = new MutableKey(42);
Map<MutableKey, String> map = new HashMap<>();
map.put(key, "Yes Album");

// The key mutates
key.id = 90125;

// Now you cannont retrieve the album
System.out.println(map.get(key)); 

// Output: null

Right 👉

class ImmutableKey {
    private final int id;

    ImmutableKey(int newId) {
        this.id = newId;
    }

    @Override
    public int hashCode() {
        return this.id;
    }

    @Override
    public boolean equals(Object objectToCompare) {
        if (this == objectToCompare) return true;
        ImmutableKey that = (ImmutableKey) objectToCompare;
        return id == that.id;
    }
}

ImmutableKey key = new ImmutableKey(42);
Map<ImmutableKey, String> map = new HashMap<>();
map.put(key, "Yes Album");

System.out.println(map.get(key)); 
// Output: Yes Album

Detection 🔍

  • Semi-Automatic


You can detect this smell by checking if you use mutable objects as keys in hash-based collections.


Automated tools like linters or IDE inspections can also flag mutable keys.

Tags 🏷️

  • Mutability

Level 🔋

  • Intermediate

Why the Bijection Is Important 🗺️

The bijection between the real world and your program is important because it ensures that your objects accurately reflect the relationships they are supposed to represent.


In the real world, keys are often immutable (e.g., IDs, names).


When you model these keys as mutable objects, you break the one-to-one correspondence between the real world and your program in the MAPPER.


When you break this bijection using mutable keys, you make the map's inconsistent leading to retrieval failures and unexpected behavior.

AI Generation 🤖

AI generators might create this smell if they generate mutable objects as keys without considering the implications.


This is seldom the case since AI generators suffer from primitive obsession.

AI Detection 🥃

AI generators can detect this smell with instructions to analyze the use of mutable objects in hash-based collections and flag potential issues.


You can instruct the AI to look for classes without final fields or methods that modify the object's state after creation.

Try Them! 🛠

Remember: AI Assistants make lots of mistakes

Without Proper Instructions

With Specific Instructions

ChatGPT

ChatGPT

Claude

Claude

Perplexity

Perplexity

Copilot

Copilot

Gemini

Gemini

DeepSeek

DeepSeek

Meta AI

Meta AI

Conclusion 🏁

When you use mutable objects as keys, you risk breaking the contract between the key's state and hash code.


Use immutable objects to avoid this issue.

Relations 👩‍❤️‍💋‍👨

https://hackernoon.com/how-to-find-the-stinky-parts-of-your-code-part-xxxiv

https://hackernoon.com/how-to-find-the-stinky-parts-of-your-code-part-xxv

https://hackernoon.com/how-to-find-the-stinky-parts-of-your-code-part-xxiv

https://hackernoon.com/how-to-find-the-stinky-parts-of-your-code-part-xxxvi

https://hackernoon.com/how-to-find-the-stinky-parts-of-your-code-part-xviii

https://hackernoon.com/how-to-find-the-stinky-parts-of-your-code-part-xxvi

https://hackernoon.com/how-to-find-the-stinky-parts-of-your-code-part-xlii

More Information 📕

Disclaimer 📘

Code Smells are my opinion.

Credits 🙏

Photo by Kathyryn Tripp on Unsplash


The most important property of a program is whether it accomplishes the intention of its user.


C.A.R. Hoare



This article is part of the CodeSmell Series.