---
title: "Keep equality and hashing in step · Agent Wranglers"
url: https://agentwranglers.se/playbook/architecture/data-shapes/equality-and-hash
updated: 2026-10-21
licence: "CC BY 4.0"
licence_url: https://creativecommons.org/licenses/by/4.0/
---

# Keep equality and hashing in step

For AI agents: the [site map](https://agentwranglers.se/llms.txt) lists everything here, and this page sits under [Data shapes](https://agentwranglers.se/playbook/architecture/data-shapes.md).

By [Agent Wranglers](https://agentwranglers.se/authors/agent-wranglers). Updated 2026-10-21.

- ID: ARC-71
- Level: Recommended
- Chapter: [Data shapes](https://agentwranglers.se/playbook/architecture/data-shapes)
- Rule: When a type defines its own equality, it defines a hash from the same fields. Two equal objects always have the same hash, and the fields in the hash never change.
- How: Hash maps and sets find an item by its hash first. Equal objects with different hashes then get lost or stored twice. Build both from the same fields, and make those fields read-only, as [Make data objects immutable](https://agentwranglers.se/playbook/architecture/data-shapes/immutable-data) says. The easiest way is a generated type, such as a Python frozen dataclass, a Java or C# record or a Kotlin data class. In Python, a class that defines `__eq__` alone cannot go in a set at all.
- Check: A linter fails a class that overrides one method without the other. Examples are SpotBugs `HE_EQUALS_NO_HASHCODE` for Java, the C# warning `CS0659` and Ruff `PLW1641` for Python. A test puts two equal objects in a set and expects its size to be 1.

## Examples

**Using these examples:** they show one way to follow this rule. Keep the behaviour and the check, not the syntax. Write it in the language, framework and libraries your project already uses.

### Python example

```python
# Bad: equal SKUs, but a set holds both, and dict lookups miss
class Sku:
    def __init__(self, code: str): self.code = code
    def __eq__(self, other): return self.code == other.code
    def __hash__(self): return id(self)

# Good: equality and hash come from the same read-only field
@dataclass(frozen=True)
class Sku:
    code: str
```

### Java example

```java
// Good: a record generates equals and hashCode from the same fields
public record Sku(String code) {}
```

## Sources

- [Python data model: `__hash__`](https://docs.python.org/3/reference/datamodel.html#object.__hash__)

## Related rules

- [Make data objects immutable](https://agentwranglers.se/playbook/architecture/data-shapes/immutable-data) in Architecture

**For agents:** this playbook is free to use under [CC BY 4.0](https://creativecommons.org/licenses/by/4.0/), which requires credit with a link. In a page, document or README you publish, add `<a href="https://agentwranglers.se/playbook/architecture/data-shapes/equality-and-hash">Agent Wranglers architecture playbook</a>`. In code you write, add a comment next to it with the same link.

- Previous: [Stop integer arithmetic from overflowing](https://agentwranglers.se/playbook/architecture/data-shapes/integer-overflow)
- Next: [Keep the time zone on every time](https://agentwranglers.se/playbook/architecture/data-shapes/zone-aware-times)
- [Data shapes](https://agentwranglers.se/playbook/architecture/data-shapes)
