Koray Tugay
Koray Tugay

Reputation: 23800

How can I sort a Map by some property of the key in the map?

Lets say we have a class called Activity:

public class Activity {
    private Project project;
}

So every Activity Object will have a Project.. Also every Project has a Unit. Two different Projects can have the same unit:

public class Project {
    private String projectName; 
    private Unit unit;
}

And a Unit has a name as well:

public class Unit {
    private String unitName;
}

So currently I have a TreeMap like this:

SortedMap<Project,List<Activity> myMap

One example may be:

projectOne -> [activityOne,activityTwo,activityThree]
projectTwo -> [activityThree,activityFouractivityFive]
projectThree -> [activityFour,activityFive,activitySix]

etc...

Now lets assume projectOne has the same unit with projectThree (unitAAA) and projectTwo has unitZZZ...

I want to sort my Map alphabetically by the unit of the project elements:

projectOne -> [...]
projectThree -> [...] 
projectTwo -> [...]

How can I achieve this? I know in Stackoverflow the question is What have you tried so far?, well I am really stuck in this point so I have not really tried anything besides trying to come up with what I can even try..

Upvotes: 0

Views: 1576

Answers (2)

Thomas
Thomas

Reputation: 88707

A map is basically an unsorted collection but there are sorted maps as well, e.g. TreeMap. In that case provide a comparator which sorts projects based on their to the constructor:

SortedMap<Project, List<Activity>> myMap = new TreeMap<>( new Comparator<Project>() {
  public int compare( Project lhs, Project rhs) {

    int r = lhs.unit.unitName.compareTo(rhs.unit.unitName); //note that null checks etc. are omitted for simplicity, don't forget them in your code unless you know for sure that unit and unitName can't be null   
    if( r == 0 && !lhs.equals(rhs)) {
      //take other properties into account for consistent behavior with equals()
      //see "Update 2" below
    }
    return r;
  }
});

Note that if you need to sort the map using a different comparator (or can't provide a comparator) you'd have to create a list using the entries of the map and sort that.

Something like this:

 List<Map.Entry<Project, List<Activity>> l = new ArrayList<>(myMap.entrySet());
 Collections.sort(l, new Comparator<Map.Entry<Project, List<Activity>>() {
   public int compare( Map.Entry<Project, List<Activity> lhs, Map.Entry<Project, List<Activity> rhs) {
    return lhs.getKey().unit.unitName.compareTo(rhs.getKey().unit.unitName);
  }
 });

Also note that it is not possible for a collection or sorted map to have different sort orders, i.e. you can only provide one comparator or natural ordering for the elements.

In any case you'd either have to change the sort order of the collection (e.g. by using Collections.sort(...) or, if you need to maintain multiple orders simultanously, use multiple collections (which can be sorted views to a base collection/map).

Update I'll add an example for a copy of the TreeMap:

//new TreeMap like above
SortedMap<Project, List<Activity>> copy = new TreeMap<>( new Comparator<Project>() { ... } );
copy.putAll( myMap );

Update 2

As for the comparator, note that it is required to be consistent with equals, i.e. the comparator must only return 0 if both objects are equal. Thus you'd need to take other properties of a Project into account if the units are equal. Without that, if two projects use the same unit, they are considered equal by the TreeMap and thus entries might get lost.

For more information see: What does comparison being consistent with equals mean ? What can possibly happen if my class doesn't follow this principle?

The compare method might look like this, if the project names are unique:

public int compare( Project lhs, Project rhs) {
  //as above null checks etc. are omitted for simplicity's sake
  int r = lhs.unit.unitName.compareTo(rhs.unit.unitName);
  if( r == 0 && !lhs.equals(rhs)) {
    r = lhs.projectName.compareTo( rhs.projectName );

    //you could also use the natural ordering of the projects here:
    //r = lhs.compareTo( rhs );
  }
  return r;
}

Upvotes: 4

Smutje
Smutje

Reputation: 18143

A HashMap can't be sorted, but if you want to display the contents of the map sorted, you could sort a list of your projects by unit (look for a Comparator on Project) and get the value of the corresponding project from the map.

Upvotes: 1

Related Questions