Reputation: 181
For example, if I have a map consists of {"A", 0.0}, {"B", 3.14}, {"C", 3.14}, {"D", 8.8}, {"E", 2.1}, {"F", 1.01}
and the top 3 keys would be {"D", "B", "C"}
.
I know the procedural way to do this but is there a smarter/functional way to do it in Java 8?
Edit: note that we can put each element of the map into a priority queue of size N, so the time complexity should be Mlog(N), faster than sorting all M elements which is Mlog(M).
Edit 2: As requested, this is what I've got:
public static void main(String args[]) {
final Map<String, Double> map = new HashMap<String, Double>() {{
put("A", 0.0);
put("B", 3.14);
put("C", 3.14);
put("D", 8.8);
put("E", 2.1);
put("F", 1.01);
}};
System.out.println("answer= " + getTopN(map, 3).toString());
}
static List<String> getTopN(final Map<String, Double> map, int n) {
// Creating priority queue with limit size n
PriorityQueue<Entry<String, Double>> pq = new PriorityQueue<>(n, Entry.comparingByValue());
for (Entry<String, Double> entry : map.entrySet()) {
pq.add(entry);
if (pq.size() > n) {
pq.poll();
}
}
Stack<String> stack = new Stack<>();
while (!pq.isEmpty()) {
stack.add(pq.poll().getKey());
}
final ArrayList<String> answer = new ArrayList<>();
while (!stack.isEmpty() && n-- > 0) {
answer.add(stack.pop());
}
return answer;
}
Upvotes: 2
Views: 1467
Reputation: 34460
Your code can be improved by making use of TreeSet
(instead of PriorityQueue
and Stack
):
static List<String> getTopN(Map<String, Double> map, int n) {
TreeSet<Map.Entry<String, Double>> topN = new TreeSet<>(
Map.Entry.<String, Double>comparingByValue()
.reversed() // by value descending, then by key
.thenComparing(Map.Entry::getKey)); // to allow entries with repeated values
map.entrySet().forEach(e -> {
topN.add(e);
if (topN.size() > n) topN.pollLast();
});
return topN.stream()
.map(Map.Entry::getKey)
.collect(Collectors.toList());
}
Note that I'm using a comparator that sorts the TreeSet
of entries by value in descending order and then by key. This is to make it possible for the set to contain entries with equal values.
The TreeSet.pollLast()
method is the equivalent of PriorityQueue.poll()
method.
Upvotes: 1
Reputation: 45329
Here's a way that uses a reverse Double comparator by entry value:
Map<String, Double> map = new HashMap<>();
map.put("A", 0.0);
map.put("B", 3.14);
map.put("C", 3.14);
map.put("D", 8.8);
map.put("E", 2.1);
map.put("F", 1.01);
List<String> topKeys = map.entrySet().stream()
.sorted(Comparator.<Entry<String, Double>>comparingDouble(Entry::getValue)
.reversed())
.limit(3) //limit to 3
.map(Entry::getKey)
.collect(Collectors.toList());
The returned list contains [D, B, C]
Upvotes: 3