I am relatively new to Java, and often find that I need to sort a Map<Key, Value>
on the values.
Since the values are not unique, I find myself converting the keySet
into an array
, and sorting that array through array sort with a custom comparator that sorts on the value associated with the key.
Is there an easier way?
This is a variation of Anthony's answer, which doesn't work if there are duplicate values:
Note that it's rather up in the air how to handle nulls.
One important advantage of this approach is that it actually returns a Map, unlike some of the other solutions offered here.
Some simple changes in order to have a sorted map with pairs that have duplicate values. In the compare method (class ValueComparator) when values are equal do not return 0 but return the result of comparing the 2 keys. Keys are distinct in a map so you succeed to keep duplicate values (which are sorted by keys by the way). So the above example could be modified like this:
To accomplish this with the new features in Java 8:
The entries are ordered by their values using the given comparator. Alternatively, if your values are mutually comparable, no explicit comparator is needed:
The returned list is a snapshot of the given map at the time this method is called, so neither will reflect subsequent changes to the other. For a live iterable view of the map:
The returned iterable creates a fresh snapshot of the given map each time it's iterated, so barring concurrent modification, it will always reflect the current state of the map.
Three 1-line answers...
I would use
Google CollectionsGuava to do this - if your values areComparable
then you can useWhich will create a function (object) for the map [that takes any of the keys as input, returning the respective value], and then apply natural (comparable) ordering to them [the values].
If they're not comparable, then you'll need to do something along the lines of
These may be applied to a TreeMap (as
Ordering
extendsComparator
), or a LinkedHashMap after some sortingNB: If you are going to use a TreeMap, remember that if a comparison == 0, then the item is already in the list (which will happen if you have multiple values that compare the same). To alleviate this, you could add your key to the comparator like so (presuming that your keys and values are
Comparable
):= Apply natural ordering to the value mapped by the key, and compound that with the natural ordering of the key
Note that this will still not work if your keys compare to 0, but this should be sufficient for most
comparable
items (ashashCode
,equals
andcompareTo
are often in sync...)See Ordering.onResultOf() and Functions.forMap().
Implementation
So now that we've got a comparator that does what we want, we need to get a result from it.
Now this will most likely work work, but:
TreeMap
; there's no point trying to compare an inserted key when it doesn't have a value until after the put, i.e., it will break really fastPoint 1 is a bit of a deal-breaker for me; google collections is incredibly lazy (which is good: you can do pretty much every operation in an instant; the real work is done when you start using the result), and this requires copying a whole map!
"Full" answer/Live sorted map by values
Don't worry though; if you were obsessed enough with having a "live" map sorted in this manner, you could solve not one but both(!) of the above issues with something crazy like the following:
Note: This has changed significantly in June 2012 - the previous code could never work: an internal HashMap is required to lookup the values without creating an infinite loop between the
TreeMap.get()
->compare()
andcompare()
->get()
When we put, we ensure that the hash map has the value for the comparator, and then put to the TreeSet for sorting. But before that we check the hash map to see that the key is not actually a duplicate. Also, the comparator that we create will also include the key so that duplicate values don't delete the non-duplicate keys (due to == comparison). These 2 items are vital for ensuring the map contract is kept; if you think you don't want that, then you're almost at the point of reversing the map entirely (to
Map<V,K>
).The constructor would need to be called as
The answer voted for the most does not work when you have 2 items that equals. the TreeMap leaves equal values out.
the exmaple: unsorted map
results
So leaves out E!!
For me it worked fine to adjust the comparator, if it equals do not return 0 but -1.
in the example:
now it returns:
unsorted map:
results:
as a response to Aliens (2011 nov. 22): I Am using this solution for a map of Integer Id's and names, but the idea is the same, so might be the code above is not correct (I will write it in a test and give you the correct code), this is the code for a Map sorting, based on the solution above:
and this is the test class (I just tested it, and this works for the Integer, String Map:
here is the code for the Comparator of a Map:
and this is the testcase for this:
of cource you can make this a lot more generic, but I just needed it for 1 case (the Map)
I've merged the solutions of user157196 and Carter Page: