user2442072
user2442072

Reputation: 457

How to code a custom comparator to sort a SortedMap?

I'm new to Java and learning about Collections. As an exercise, I'm trying to sort a SortedMap using a custom Comparator. I'd like to be able sort the students SortedMap by average of grades in descending order.

SortedMap <String, Student> students = new TreeMap <String, Student> ();

public class Student {
    private String name;
    private List<Integer> marks = new ArrayList<Integer>();

    public Student(String n) {
        this.name = n;
    }

    public String getName() {
        return this.name;
    }

    public Double getAvg() {
        return this.getStats()
                .getAverage();
    }

    public void addMark(Integer mark) {
        this.marks.add(mark);
    }

    public DoubleSummaryStatistics getStats() {
        return this.marks.stream()
                .mapToDouble(mark -> mark)
                .summaryStatistics();
    }

    @Override
    public String toString() {
        return this.name + " " + getStats().getCount() + " " + getStats().getAverage();
    }
}

I was able to figure out how to build a custom comparator that returns a set:

SortedSet<Map.Entry<String, Student>> meritSet = new TreeSet<Map.Entry<String, Student>>(
    new Comparator<Map.Entry<String, Student>>() {
        @Override
        public int compare(Map.Entry<String, Student> s1,
            Map.Entry<String, Student> s2) {

            Double s1Avg = students.get(s1.getKey()).getStats().getAverage();
            Double s2Avg = students.get(s2.getKey()).getStats().getAverage();

            return s1Avg.compareTo(s2Avg);
        }
    }
);

meritSet.addAll(students.entrySet());

How would I code a custom comparator that returns a SortedMap and not a SortedSet?

Upvotes: 1

Views: 180

Answers (2)

deHaar
deHaar

Reputation: 18568

What you can do in order to use a TreeMap which implements SortedMap is make the Student implement Comparable like this:

public class Student implements Comparable<Student> {

    /*
     * nearly the entire class stays untouched,
     * except from the class declaration and the additional method below
     */

    @Override
    public int compareTo(Student o) {
        return this.getAvg().compareTo(o.getAvg());
    }

    // you could and probably should implement equals(Student o) and hashCode() as well
}

This will automatically sort the entries in a TreeMap<Student, SomethingElse> by the values of the method getAvg() (using the method compareTo of Double).

The TreeMap will be sorted by its keys... So consider using a Map that takes Students as keys.

If you now want to have a SortedSet, like a TreeSet<Student> it will be sorted accordingly, just by having implemented Comparable<Student>.

Upvotes: 1

Ward
Ward

Reputation: 2828

What you want to do is sort a Map based on it's values. This is however not something that can be achieved with a SortedMap.

A Map that further provides a total ordering on its keys.

If you just want to store them sorted in a Map, without requiring the Map to still be sorted when you would add new values, you could store all entries in a LinkedHashMap:

Map<String, Studen> meritMap = meritSet.stream()
        .collect(toMap(Map.Entry::getKey, Map.Entry::getValue, (a, b) -> a, LinkedHashMap::new));

This won't give you a SortedMap though, but the Map will be sorted.

Upvotes: 3

Related Questions