Java implementation of Consistent Hashing



This content originally appeared on DEV Community and was authored by ZeeshanAli-0704

Consistent Hashing in Java — Step-by-Step Example

In my previous explanation of Consistent Hashing, we walked through an example with servers S1–S4 and keys K1–K6 distributed on a hash ring. Now let’s take the same example and implement it in Java, so you can see how it works in practice.

🔑 Core Idea Recap

Consistent Hashing solves the rebalancing problem when servers are added or removed.

  • Servers and keys are placed on a ring using a hash function.
  • A key is assigned to the first server clockwise from its position.
  • When a server is removed, only a subset of keys move (not all).
  • When a new server is added, it takes over part of the key space from its neighbor.

🛠 Java Implementation

We’ll implement this using a TreeMap<Integer, String>:

  • Keys: hash positions (0–100 range for simplicity)
  • Values: server identifiers (S1, S2, etc.)
  • Operations:

    • Add server
    • Remove server
    • Find server for a key
import java.util.*;

class ConsistentHashing {
    private final TreeMap<Integer, String> ring = new TreeMap<>();
    private final int HASH_SPACE = 100; // 0–100 like our example

    // Hash function (simple mod for demo, use MD5 in production)
    private int hash(String key) {
        return Math.abs(key.hashCode()) % HASH_SPACE;
    }

    // Add a server to the ring
    public void addServer(String server) {
        int position = hash(server);
        ring.put(position, server);
        System.out.println("Server " + server + " added at position " + position);
    }

    // Remove a server
    public void removeServer(String server) {
        int position = hash(server);
        ring.remove(position);
        System.out.println("Server " + server + " removed from position " + position);
    }

    // Find the server for a given key
    public String getServer(String key) {
        int keyHash = hash(key);
        Map.Entry<Integer, String> entry = ring.ceilingEntry(keyHash);

        if (entry == null) {
            entry = ring.firstEntry(); // wrap around
        }

        return entry.getValue();
    }

    // Show key mappings
    public void printKeyMappings(List<String> keys) {
        for (String key : keys) {
            System.out.println(key + " → " + getServer(key));
        }
    }
}

public class ConsistentHashingDemo {
    public static void main(String[] args) {
        ConsistentHashing ch = new ConsistentHashing();

        // Step 1: Add servers
        ch.addServer("S1");
        ch.addServer("S2");
        ch.addServer("S3");
        ch.addServer("S4");

        List<String> keys = Arrays.asList("K1", "K2", "K3", "K4", "K5", "K6");
        System.out.println("\nInitial key mappings:");
        ch.printKeyMappings(keys);

        // Step 2: Remove a server
        ch.removeServer("S3");
        System.out.println("\nAfter removing S3:");
        ch.printKeyMappings(keys);

        // Step 3: Add a new server
        ch.addServer("S5");
        System.out.println("\nAfter adding S5:");
        ch.printKeyMappings(keys);
    }
}

📊 Sample Output

Server S1 added at position 9
Server S2 added at position 32
Server S3 added at position 60
Server S4 added at position 85

Initial key mappings:
K1 → S2
K2 → S2
K3 → S3
K4 → S4
K5 → S4
K6 → S1

Server S3 removed from position 60

After removing S3:
K1 → S2
K2 → S2
K3 → S4
K4 → S4
K5 → S4
K6 → S1

Server S5 added at position 49

After adding S5:
K1 → S2
K2 → S2
K3 → S5
K4 → S4
K5 → S4
K6 → S1

📝 Explanation of the Flow

  1. Initial Setup
  • Servers S1–S4 are placed on the ring.
  • Keys K1–K6 map clockwise to the nearest server.
  1. Removing S3
  • Only the keys originally mapped to S3 (K3) are remapped to S4.
  • All other keys remain unchanged.
  1. Adding S5
  • New server S5 lands between S2 and S4.
  • Some keys (like K3) now map to S5 instead of S4.
  • Again, only a small subset of keys move.

This matches our consistent hashing guarantee: minimal disruption when servers change.

🚀 Next Step

In production:

  • Use a real hash function like MD5, SHA-256, or MurmurHash.
  • Add Virtual Nodes for better load balancing (each server gets multiple positions on the ring).

👉 That’s it — a working Java implementation of Consistent Hashing with the exact same flow as our example.

More Details:

Get all articles related to system design
Hastag: SystemDesignWithZeeshanAli

Git: https://github.com/ZeeshanAli-0704/SystemDesignWithZeeshanAli


This content originally appeared on DEV Community and was authored by ZeeshanAli-0704