Sorting custom data structure on Key in TreeMap

10,417

The problem is that your implementation of compareTo is not consistent with equals, which is required by TreeMap. From the API docs:

Note that the ordering maintained by a sorted map (whether or not an explicit comparator is provided) must be consistent with equals if this sorted map is to correctly implement the Map interface.

One possible consistent implementation would be to first compare by rank and then by name if the rank values are equal. For two instances of Custom with equal ranks and identical names you should not expect to be able to store them both as keys within the same Map - This violates the contract of Map.

public int compareTo(Custom o) {
  int ret = this.rank - o.rank;

  // Equal rank so fall back to comparing by name.
  if (ret == 0) {
    ret = this.name.compareTo(o.name);
  }

  return ret;
}
Share:
10,417
Kunal
Author by

Kunal

Updated on June 14, 2022

Comments

  • Kunal
    Kunal almost 2 years

    I am trying to sort a TreeMap on key. Key is some custom DataStructure having int, List, String, etc. The member on which I am expecting a sort has some duplicates. Let's say that member is Rank. More than 1 object can have same rank.

    Simplified version example:

    NOTE: in the CompareTo method below 0 is not returned intentionally to NOT ignore duplicates.(Please correct me if this is not the right way to avoid duplicates)

    import java.util.TreeMap;
    
    
    public class TreeTest {
    
    public static void main(String[] args) {
        TreeMap<Custom,String> t = new TreeMap<Custom,String>();
        Custom c1 = new Custom();
        c1.setName("a");
        c1.setRank(0);
    
        Custom c2 = new Custom();
        c2.setName("b");
        c2.setRank(1);
    
        Custom c3 = new Custom();
        c3.setName("c");
        c3.setRank(0);
    
        t.put(c1, "first");
        t.put(c2, "Second");
        t.put(c3, "Third");
    
        System.out.println(t.keySet());
    
        for(Custom c:t.keySet()){
            System.out.println(t.get(c));
        }
      }
    }
    

    And Custom Object

    package com.example.ui;
    
     public class Custom implements Comparable<Custom>{
    
    int rank;
    String name;
    
    public int getRank() {
        return rank;
    }
    
    public void setRank(int rank) {
        this.rank = rank;
    }
    
    public String getName() {
        return name;
    }
    
    public void setName(String name) {
        this.name = name;
    }
    
    
    
    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + ((name == null) ? 0 : name.hashCode());
        result = prime * result + rank;
        return result;
    }
    
    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        Custom other = (Custom) obj;
        if (name == null) {
            if (other.name != null)
                return false;
        } else if (!name.equals(other.name))
            return false;
        if (rank != other.rank)
            return false;
        return true;
    }
    
        // 0 is not returned intentionally to NOT ignore duplicates.
     public int compareTo(Custom o) {
        if(o.rank>this.rank)
            return 1;
        if(o.rank==this.rank)
            return -1;
        return -1;
     }
     }
    

    Output::

    [com.example.ui.Custom@fa0, com.example.ui.Custom@fbe, com.example.ui.Custom@f80]
    null
    null
    null
    

    Expected: First, Second, Third based on Rank 0,1,0 respectively.

    I looked at couple of examples on Google. Most of them were basic usage on TreeMap sort using keys or values with primitive datatypes, but none with duplicates when sorting member is a part of custom key DataStructure.

    Please help?