Search code examples
javahashmaptreemap

Java TreeMap put vs HashMap put, custom Object as key


My objective was to use the TreeMap to make Box key objects sorted by Box.volume attribute while able to put keys distinct by the Box.code. Is it not possible in TreeMap?

As per below test 1, HashMap put works as expected, HashMap keeps both A, B key objects, but in test 2, TreeMap put doesn't treat D as a distinct key, it replaces C's value, note that i used the TreeMap comparator as Box.volume, because i want keys to be sorted by volume in TreeMap.

import java.util.*;

public class MapExample {
    public static void main(String[] args) {
        //test 1
        Box b1 = new Box("A");
        Box b2 = new Box("B");
        Map<Box, String> hashMap = new HashMap<>();
        hashMap.put(b1, "test1");
        hashMap.put(b2, "test2");
        hashMap.entrySet().stream().forEach(o-> System.out.println(o.getKey().code+":"+o.getValue()));
        //output
        A:test1
        B:test2

        //test 2
        Box b3 = new Box("C");
        Box b4 = new Box("D");
        Map<Box, String> treeMap = new TreeMap<>((a,b)-> Integer.compare(a.volume, b.volume));
        treeMap.put(b3, "test3");
        treeMap.put(b4, "test4");
        treeMap.entrySet().stream().forEach(o-> System.out.println(o.getKey().code+":"+o.getValue()));
        //output
        C:test4
    }
}

class Box {
    String code;
    int volume;

    public Box(String code) {
        this.code = code;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Box box = (Box) o;
        return code.equals(box.code);
    }

    @Override
    public int hashCode() {
        return Objects.hash(code);
    }
}

Thank you


Solution

  • TreeMap considers 2 keys for which the comparison method returns 0 to be identical, even if they are not equal to each other, so your current TreeMap cannot contain two keys with the same volume.

    If you want to keep the ordering by volume and still have multiple keys with the same volume in your Map, change your Comparator's comparison method to compare the Box's codes when the volumes are equal. This way it will only return 0 if the keys are equal.

    Map<Box, String> treeMap = new TreeMap<>((a,b)-> a.volume != b.volume ? Integer.compare(a.volume, b.volume) : a.code.compareTo(b.code));
    

    Now the output is:

    C:test3
    D:test4