I had to code my own ´Filter` for a medal table, which I put in a
HashMap <String, MedalsOfCountry> unsortedList
.
The key
is the code of a country, for example:”USA” and the MedalsOfCountry
is the object for the specific country with the fields: goldCount
, silverCount
, bronzeCount
. MedalsOfCountry
is a static inner class, which I coded and looks like this:
static class MedalsOfCountry extends Filter { int goldCount; int silverCount; int bronzeCount; Medal medalType; MedalsOfCountry(Medal m, int count) { medalType = m; if (medalType == Medal.GOLD) { goldCount += count; } else if (medalType == Medal.SILVER) { silverCount+= count; } else { bronzeCount += count; } } public int compareTo(MedalOfCountry medals) { if(this.goldCount == medals.goldCount) { return this.silverCount - medals.silverCount; } else if(this.silverCount == medals.silverCount) { return this.bronzeCount - medals.bronzeCount; } return this.goldCount - medals.goldCount; }
Anyway I tried to sort my HashMap like this:
Map<String, MedalsOfCountry> sortedList = new TreeMap<String, MedalsOfCountry>(new Comparator<String>() { @Override public int compare(String land1, String land2) { return unsortedList.get(land1).compareTo(unsortedList.get(land2)); }
Why does my sorting not work?
Answer
A TreeMap is sorted by keys:
The map is sorted according to the natural ordering of its keys, or by a Comparator provided at map creation time, depending on which constructor is used.
So using a TreeMap would not be the best choice in your case. You could either use a LinkedHashMap
or changing your map to a List
.
A LinkedHashMap is sorted by insertion order:
This linked list defines the iteration ordering, which is normally the order in which keys were inserted into the map (insertion-order).
Map<String, MedalsOfCountry> sorted = unsorted.entrySet().stream() .sorted((entry0, entry1) -> entry0.getValue().compareTo(entry1.getValue())) .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (v0, v1) -> { throw new IllegalArgumentException("duplicate keys are not possible here"); }, LinkedHashMap::new));
To improve this I would recommend to implement the Comparable interface in your Object class:
static class MedalsOfCountry implements Comparable<MedalsOfCountry> { int goldCount; int silverCount; int bronzeCount; MedalsOfCountry(int count) { goldCount += count; } @Override public int compareTo(MedalsOfCountry medals) { int compareGold = this.goldCount - medals.goldCount; if (compareGold != 0) { return compareGold; } int compareSilver = this.silverCount - medals.silverCount; if (compareSilver != 0) { return compareSilver; } return this.bronzeCount - medals.bronzeCount; } }
You then can use the following to create the sorted map:
Map<String, MedalsOfCountry> sorted = unsorted.entrySet().stream() .sorted(Comparator.comparing(Map.Entry::getValue)) .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (v0, v1) -> { throw new IllegalArgumentException("duplicate keys are not possible here"); }, LinkedHashMap::new));
Additionally you can improve your compareTo()
method by using a comparator to define multiple steps:
@Override public int compareTo(MedalsOfCountry medals) { return Comparator.comparingInt(MedalsOfCountry::getGoldCount) .thenComparingInt(MedalsOfCountry::getSilverCount) .thenComparingInt(MedalsOfCountry::getBronzeCount) .compare(this, medals); }
To also can use the Comparator.reversed()
method to reverse the order of your stream:
Map<String, MedalsOfCountry> sorted = unsorted.entrySet().stream() .sorted(Comparator.comparing(Map.Entry<String, MedalsOfCountry>::getValue).reversed()) .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (v0, v1) -> { throw new IllegalArgumentException("duplicate keys are not possible here"); }, LinkedHashMap::new));