How to generalize Java post sorting method to Java?
In my code, I have several maps. Then I have a method like the one below that takes one of the cards, sorts the records by their value and returns a list of the top ones, with the sum given by the parameter.
Example:
if the input card is like
- "a" = 5,
- "b" = 4,
- "c" = 8,
- "d" = 0,
and I call the method with quantity = 2 in the parameter, I am assigned a list of the two highest card entries sorted in descending order
- "c" = 8,
- "a" = 5.
Now I have one such method for each of the cards, and they differ only:
- declarations
<Type1, Type2>
throughout the method and - the collection of the
(distances.entrySet());
entire list.
Can I generalize this anyway so that I only have one method like this while being able to receive any types?
private static Map<String, Double> distances = new TreeMap<>();
private static Map<String, Integer> titles = new TreeMap<>();
private static Map<Integer, Integer> hours = new TreeMap<>();
private static Map<Date, Integer> days = new TreeMap<>();
public static List<Entry<String, Double>> getTopDistances(int quantity) {
List<Map.Entry<String, Double>> all = new ArrayList<>(distances.entrySet());
List<Map.Entry<String, Double>> requested = new ArrayList<>();
Collections.sort(all, new Comparator<Map.Entry<String, Double>>() {
@Override
public int compare(Entry<String, Double> e1, Entry<String, Double> e2) {
return (e2.getValue().compareTo(e1.getValue()));
}
});
int i = 0;
while (all.iterator().hasNext() && ++i <= quantity) {
requested.add(all.get(i - 1));
}
return requested;
}
I can of course continue with all the methods separated, but I feel like a better way to do it. Have explored generics, wildcards, collections and interfaces which I think is the way to go, but I still need a push.
source to share
With Java streams, one single line can create whatever you want from the map to the final list. Bellow I'll package it in a private method, which is a little more tidy, but you can inline it if you like.
Since all your values ββare Comparable
:
private <K, V extends Comparable<V>> List<Map.Entry<K,V>> getTop(Map<K,V> map, int quantity) {
return map.entrySet().stream()
.sorted((a,b) -> b.getValue().compareTo(a.getValue()))
.limit(quantity)
.collect(Collectors.toList());
}
If the value type was not comparable, you need to pass the comparator as an additional parameter:
private <K, V> List<Map.Entry<K,V>> getTop(Map<K,V> map, Comparator<V> cmp, int quantity) {
return map.entrySet().stream()
.sorted((a,b) -> cmp.compare(b,a))
.limit(quantity)
.collect(Collectors.toList());
}
source to share
You cannot create a comparator that will compare two being compared with different types, so you must do it like this:
private static Map<String, Double> distances = new TreeMap<>();
private static Map<String, Integer> titles = new TreeMap<>();
private static Map<Integer, Integer> hours = new TreeMap<>();
private static Map<Date, Integer> days = new TreeMap<>();
public static List<Map.Entry<String, Double>> getTopDistances(int quantity) {
List<Map.Entry<String, Double>> all = new ArrayList<>(distances.entrySet());
List<Map.Entry<String, Double>> requested = new ArrayList<>();
all.sort(naturalOrder());
int i = 0;
while (all.iterator().hasNext() && ++i <= quantity) {
requested.add(all.get(i - 1));
}
return requested;
}
public static <T extends Comparable<? super T>> Comparator<Map.Entry<?,T> naturalOrder() {
return (e1, e2) -> e2.getValue().compareTo(e2.getValue());
}
source to share