0

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..

Koray Tugay
  • 22,894
  • 45
  • 188
  • 319

2 Answers2

4

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;
}
Community
  • 1
  • 1
Thomas
  • 87,414
  • 12
  • 119
  • 157
  • Yes, sorry it is already a TreeMap. But how can I do the conversion? – Koray Tugay Mar 04 '14 at 11:52
  • @KorayTugay by properly implementing [compareTo](http://docs.oracle.com/javase/7/docs/api/java/lang/Comparable.html#compareTo(T)) in the Class `Project` of passing a [Comparator](http://docs.oracle.com/javase/7/docs/api/java/util/Comparator.html) at creation time of the TreeMap. – A4L Mar 04 '14 at 11:55
  • @A4L The tree map is returned from a method that is used in many other places. I can not pass a Comparator to it. I only have the data structure and I must do the modification myself. – Koray Tugay Mar 04 '14 at 11:56
  • @KorayTugay So you need go with nr. 1, i.e. Implement `compareTo` in which case whoever puts an object of `Project` it gets sorted the way you want. If you dont want this to happen or cannot do it bacause you can change any of the classes then you might consider copying into a new TreeMap where you can pass a `Comparator` – A4L Mar 04 '14 at 12:02
  • @KorayTugay, then add the comparison logic in your `Project` class by making it implement `Comparable` and override 'compareTo(Project o)' in it. – Zeeshan Mar 04 '14 at 12:02
  • @Zeeshan I have no power on Projet class either. – Koray Tugay Mar 04 '14 at 12:03
  • @KorayTugay Then create a new TreeMap with a custom Comparator and add all the current element in it. – Zeeshan Mar 04 '14 at 12:07
  • @KorayTugay, check the updated answer by Thomas, that will work :) – Zeeshan Mar 04 '14 at 13:37
  • @Thomas Thanks for your help. However the last example you provide is not working. When I pass a Map with 8 keys (Projects), it returns me a Map with 2 keys only. I lose the entries with 6 Project Elements. – Koray Tugay Mar 04 '14 at 17:27
  • @KorayTugay I'll add an update on the comparator implementation. – Thomas Mar 05 '14 at 09:37
  • @Thomas I was here to tell you that I figured it out but I saw your update. Thank you very much. – Koray Tugay Mar 05 '14 at 10:52
1

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.

Smutje
  • 17,733
  • 4
  • 24
  • 41