I want to build a Map containing elements that are sorted by their value. I receive a list of purchases containing {customerId, purchaseAmount}, and want to build a map of the form which maps the customer to their total purchase amount. A single customer may have multiple purchases.
Finally, I want to process this information customer-by-customer, in order of decreasing total purchase amount. Meaning that I process the highest spending customer first, and the lowest spending customer last.
My initial solution for this was to build a Map (using HashMap), converting this Map to a List (LinkedList), sorting this List in decreasing order, and then processing this List. This is an O(n log n) solution, and I believe it is the best possible time complexity. However, I want to know if there is some way to leverage a data structure such as TreeMap which has a sorted property inherent to it. By default it will be sorted by its keys, however I want to sort it by the value. My current solution below.
public class MessageProcessor {
public static void main(String[] args) {
List<Purchase> purchases = new ArrayList<>();
purchases.add(new Purchase(1, 10));
purchases.add(new Purchase(2, 20));
purchases.add(new Purchase(3, 10));
purchases.add(new Purchase(1, 22));
purchases.add(new Purchase(2, 100));
processPurchases(purchases);
}
private static void processPurchases(List<Purchase> purchases) {
Map<Integer, Double> map = new HashMap<>();
for(Purchase p: purchases) {
if(!map.containsKey(p.customerId)) {
map.put(p.customerId, p.purchaseAmt);
}else {
double value = map.get(p.customerId);
map.put(p.customerId, value + p.purchaseAmt);
}
}
List<Purchase> list = new LinkedList<>();
for(Map.Entry<Integer, Double> entry : map.entrySet()) {
list.add(new Purchase(entry.getKey(), entry.getValue()));
}
System.out.println(list);
Comparator<Purchase> comparator = Comparator.comparing(p -> p.getPurchaseAmt());
list.sort(comparator.reversed());
//Process list
//...
}
class Purchase {
int customerId;
double purchaseAmt;
public Purchase(int customerId, double purchaseAmt) {
this.customerId = customerId;
this.purchaseAmt = purchaseAmt;
}
public double getPurchaseAmt() {
return this.purchaseAmt;
}
}
The current code accomplishes what I want to do, however I would like to know if there is a way that I can avoid transforming the Map into a List and then sorting the List using my custom Comparator. Perhaps using some kind of sort of sorted Map. Any advice would be appreciated. Also, suggestions on how to make my code more readable or idiomatic would be appreciated. Thanks. This is my first post of StackOverflow
First of all a TreeMap does not work for you, because it is sorted by keys, not by values. Another alternative would be a LinkedHashMap. It is sorted by insertion order.
You also can use Java Streams to process your List:
Map<Integer, Double> map = purchases.stream()
.collect(Collectors.toMap(Purchase::getCustomerId, Purchase::getPurchaseAmt, (a, b) -> a + b));
This creates a map for with the customerId as key and the sum of all purchases. Next you can sort that, by using another stream and migrating it to a LinkedHashMap:
LinkedHashMap<Integer, Double> sorted = map.entrySet().stream()
.sorted(Comparator.comparing(Map.Entry<Integer, Double>::getValue).reversed())
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (a, b) -> {
throw new IllegalStateException("");
}, LinkedHashMap::new));
At the end you can create a new list again if you need it:
List<Purchase> list = sorted.entrySet().stream()
.map(e -> new Purchase(e.getKey(), e.getValue()))
.collect(Collectors.toList());
If you want more basic information to java Streams here is an official tutorial.
Related
I have a list of shop objects that are grouped by the item they have.
class Shop{
String shopName;
String item;
int size;
...}
How can I get a list of the 3 biggest shops (or n biggest shops) for each item?
ie. suppose I have
Shop("Walmart", "Hammer", 100);
Shop("Target", "Scissor", 30);
Shop("Walgreens", "Hammer", 300);
Shop("Glens", "Hammer", 500);
Shop("Walmart", "Scissor", 75);
Shop("Toms", "Hammer", 150);
I want to return a list of the top 3 shops grouped by item.
I grouped the items but i am not sure how to iterate through the given Map or entryset...
public class Shop {
int size;
String item;
String name;
public Shop(int size, String item, String name){
this.size = size;
this.item = item;
this.name = name;
}
//Return a list of the top 3 largest shops by item
public static void main(){
List<Shop> shops = new LinkedList<Shop>();
Comparator<Shop> shopComparator = new Comparator<Shop>(){
#Override
public int compare(Shop f1, Shop f2) {
return f1.getSize() < f2.getSize() ? 1 : -1;
}
};
shops.stream().collect(groupingBy(Shop::getItem))
.entrySet()
.stream()
.filter(entry -> entry.getValue().stream().map )
.forEach(item -> item.getValue())//Stuck here
;
}
}
The most important thing that you can learn about streams is that they aren't inherently "better" than equivalent approaches by any measure. Sometimes, they make code more readable, other times, less so. Use them to clarify your code, and avoid them when they obfuscate it.
This is a case where your code will be far more readable by using a collector for this purpose. Coding your own is fairly easy, and if you really want to understand streams better, I recommend it as a simple learning exercise.
Here, I'm using MoreCollectors.greatest() from the StreamEx library:
Comparator<Shop> bySize = Comparator.comparingInt(Shop::getSize);
Map<String, List<Shop>> biggestByItem
= shops.stream().collect(groupingBy(Shop::getItem, greatest(bySize, 3)));
This isn't better because it's shorter, or because it is faster and uses constant memory; it's better because complexity is factored out of the code, and hidden behind meaningful names that explain the behavior. Instead of littering your application with complex pipelines that need to be read, tested, and maintained independently, you have written (or referenced) a reusable collector with a clear behavior.
As I mentioned, there is a bit of a learning curve in understanding how the pieces of a Collector work together, but it's worth studying. Here's a possible implementation for a similar collector:
public static <T> Collector<T, ?, List<T>> top(int limit, Comparator<? super T> order) {
if (limit < 1) throw new IndexOutOfBoundsException(limit);
Objects.requireNonNull(order);
Supplier<Queue<T>> supplier = () -> new PriorityQueue<>(order);
BiConsumer<Queue<T>, T> accumulator = (q, e) -> collect(order, limit, q, e);
BinaryOperator<Queue<T>> combiner = (q1, q2) -> {
q2.forEach(e -> collect(order, limit, q1, e));
return q1;
};
Function<Queue<T>, List<T>> finisher = q -> {
List<T> list = new ArrayList<>(q);
Collections.reverse(list);
return list;
};
return Collector.of(supplier, accumulator, combiner, finisher, Collector.Characteristics.UNORDERED);
}
private static <T> void collect(Comparator<? super T> order, int limit, Queue<T> q, T e) {
if (q.size() < limit) {
q.add(e);
} else if (order.compare(e, q.peek()) > 0) {
q.remove();
q.add(e);
}
}
Given this factory, it's trivial to create others that give you bottom(3, bySize), etc.
You may be interested in this related question and its answers.
Well, you could take the following steps:
With groupingBy(Shop::getItem), you could create a map which sorts by the item, so your result would be a Map<String, List<Shop>>, where the list contains all shops with that item.
Now we need to sort the List<Shop> in reversed order, so the top items of the list are the shops with the largest size. In order to do this, we could use collectingAndThen as downstream collector to groupingBy.
Collectors.collectingAndThen(Collectors.toList(), finisherFunction);
Our finisher function should sort the list:
list -> {
Collections.sort(list, Comparator.comparing(Shop::size).reversed());
return list;
}
This would result in a Map<String, List<Shop>>, where the list is sorted, highest size first.
Now the only thing we need to do, is limiting the list size to 3. We could use subList. I think subList throws an exception if the list contains less than 3 items, so we need to use Math.min(3, list.size()) to take this into account.
list -> {
Collections.sort(list, Comparator.comparing(Shop::size).reversed());
return list.subList(0, Math.min(3, list.size()));
}
The whole code then looks like this:
shops.stream()
.collect(groupingBy(Shop::item, Collectors.collectingAndThen(Collectors.toList(), list -> {
Collections.sort(list, Comparator.comparing(Shop::size).reversed());
return list.subList(0, Math.min(3, list.size()));
})));
Online demo
Instead of 'manually' sorting the list and limiting it to 3, you could create a small class which automatically does this — both limit and sort the list upon adding elements.
Not as fancy as MC Emperor but it seems to work.
I started from the part you already did:
shops.stream().collect(Collectors.groupingBy(Shop::getItem))
.entrySet().stream().map(entry -> {
entry.setValue(entry.getValue().stream()
.sorted(Comparator.comparingInt(s->-s.size))
.limit(3) // only keep top 3
.collect(Collectors.toList()));
return entry;
}).forEach(item -> {
System.out.println(item.getKey()+":"+item.getValue());
});
You can use groupingBy along with limit to get desired result:
import static java.util.stream.Collectors.*;
// Define the sort logic. reversed() applies asc order (Default is desc)
Comparator<Shop> sortBySize = Comparator.comparingInt(Shop::getSize).reversed();
int limit = 3; // top n items
var itemToTopNShopsMap = list.stream().collect(
collectingAndThen(groupingBy(Shop::getItem),
itemToShopsMap -> getTopNShops(sortBySize, itemToShopsMap, limit)));
static Map<String, List<Shop>> getTopNShops(Comparator<Shop> sortBy, Map<String, List<Shop>> inMap, int limit) {
var returningMap = new HashMap<String, List<Shop>>();
for (var i : inMap.entrySet()) {
returningMap.put(i.getKey(), i.getValue().stream().sorted(sortBy).limit(Long.valueOf(limit)).collect(toList()));
}
return returningMap;
}
We took following steps:
Group the List by 'item'
For each grouping, i.e., item to list of shops entry, we sort the list of shops by predefined sort logic and collect (limit) the top n results.
Note:
In static method getTopNShops, mutation of source map is avoided. We could have written this method as a stream, but the stream version may have been less readable than the foreach loop.
Well, I saw other ways that involve list of arrays. Although nothing similar to this.
List<String> orderList = new ArrayList<>();
orderList.add("ARMOR");
orderList.add("ADIDAS");
orderList.add("NIKE");
I have my List<TreeMap<Brand,String>> brands returning this list.
[{NIKE=Shoes},{ADIDAS=Clothing},{ARMOR=Backpacks},{NIKE=Shorts}]
I want to sort this by the orderList provided to get the following:
[{ARMOR=Backpacks},{ADIDAS=Clothing},{NIKE=Shoes},{NIKE=Shorts}]
But I don't know how to.
Perhaps there are other ways to modify this as a list and reorder it, I am not sure.
You can convert the List<String> orderList to HashMap<String, Integer> indexMap:
["ARMOR", "ADIDAS", "NIKE"] -> {"ARMOR": 0, "ADIDAS": 1, "NIKE": 2}
Map<String, Integer> indexMap = IntStream.range(0, orderList.size()) // [0, 1, 2]
.boxed() // use int, not Integer
.collect(Collectors.toMap(i -> orderList.get(i), i -> i)); // add index to each element
Then, given TreeMap<String, String> brands you can do:
List<Map.Entry<String, String>> result = brands.entrySet()
.stream()
.filter(entry -> indexMap.contains(entry.getKey()) // Remove elements not in indexMap... and therefore also not in orderList
.sorted((a, b) -> Integer.compare(indexMap.get(a.getKey()), indexMap(b.getKey()))) // Sort all present brands to match the order from orderList
.collect(Collectors.toList());
You can use a customized Comparator directly with Collections.sort() method from the java.util.Collections API. Here is a copy-paste working example:
public class SortByList {
enum Brand {
ADIDAS, ARMOR, NIKE
}
static class BrandMap extends TreeMap<Brand, String> {
public BrandMap(Brand brand, String type) {
put(brand, type);
}
}
public static void main(String[] args) {
List<String> orderList = new ArrayList<>();
orderList.add("ARMOR");
orderList.add("ADIDAS");
orderList.add("NIKE");
List<TreeMap<Brand, String>> brands = new ArrayList<>();
brands.add(new BrandMap(Brand.NIKE, "Shoes"));
brands.add(new BrandMap(Brand.ADIDAS, "Clothing"));
brands.add(new BrandMap(Brand.ARMOR, "Backpacks"));
brands.add(new BrandMap(Brand.NIKE, "Shorts"));
brands.sort((o1, o2) -> {
Brand key1 = o1.keySet().iterator().next();
Brand key2 = o2.keySet().iterator().next();
int index1 = orderList.indexOf(key1.name());
int index2 = orderList.indexOf(key2.name());
return Integer.compare(index1, index2);
});
//[{ARMOR=Backpacks},{ADIDAS=Clothing},{NIKE=Shoes},{NIKE=Shorts}]
System.out.println(brands);
}
}
Just a note: I've created BrandMap just to easily generate the test data and populate them to the list. You can leave it and just take the Comparator implementation written in lambda expression. It should work.
Another update: If you care about performance and your orderList is likely to get bigger in time, using orderList.indexOf() inside the sort function won't do good. Because it makes a sequential search which costs O(N) for each sorting comparison:
brands.sort((o1, o2) -> {
Brand key1 = o1.keySet().iterator().next();
Brand key2 = o2.keySet().iterator().next();
// extra iteration costs extra O(N)
int index1 = orderList.indexOf(key1.name());
// extra iteration costs extra O(N)
int index2 = orderList.indexOf(key2.name());
return Integer.compare(index1, index2);
});
If this is the case, I suggest to store your orderList in a HashMap instead of a List or array if you have the chance. Alternatively, you can convert it in a one time operation and use your map before your sorting operation:
// this will cost O(N) only for once
Map<String, Integer> orderMap = new HashMap<>();
for (int i = 0; i < orderList.size(); i++) {
orderMap.put(orderList.get(i), i);
}
// then sort
brands.sort((o1, o2) -> {
Brand key1 = o1.keySet().iterator().next();
Brand key2 = o2.keySet().iterator().next();
// now use the map for getting indices in constant O(1) time
int index1 = orderMap.get(key1.name());
int index2 = orderMap.get(key2.name());
return Integer.compare(index1, index2);
});
A little extra: Surely, storing in a HashMap costs an extra O(N) space but I assume memory consumption is tolerable in your case.
You're welcome. Cheers!
I have a List of programTypes:
List<String> programTypes = {ACF, VCX, IFL}
Note: This is a map hardcoded in code.
Here, I want to attach priorities to these programTypes:
ACF->priority=2, VCX->priority=1, IFL->priority=3
What data structure should I use? Priority Queues?
Also, now I have a list of inputProgramTypes: {ABC, VCX, IFL}
I want the output to be the winningProgramType: VCX
I can code it by iterating on inputProgramTypes and setting the winningProgramType if each next has a priority greater that the set one (Like finding max problem).
But I want to know if I can optimise? And how I can use streams to write code for same to make it look clean? I am new to streams and learning my way through it.
I suggest you use an enum with a parameter constructor.
Such, the method public static Map<ProgramType, Integer> getPrioMap() will elegantly and in a type-safe way return the data structure you need.
public enum ProgramType {
ACF(2), VCX(1), IFL(3);
private int prio;
private ProgramType(int prio) {
this.prio = prio;
}
public Integer getPriority() {
return prio;
}
public static Map<ProgramType, Integer> getPrioMap() {
return List.of(ProgramType.values()).stream()
.collect(Collectors.toMap(e -> e, e -> e.getPriority()));
}
}
Possibly, a simple Map will do fine:
public Map<String, Integer> buildMapOfPriorityProgramTypes() {
Map<String, Integer> priorityProgramTypes = new HashMap<>();
priorityProgramTypes.put("ACF", 2);
priorityProgramTypes.put("VCX", 1);
priorityProgramTypes.put("IFL", 3);
return priorityProgramTypes;
}
public String getTopPriorityType(Map<String, Integer> priorityTypes) {
return priorityTypes.entrySet().stream()
.min(Map.Entry.comparingByValue())
.get().getKey();
}
If you use Java 9 or newer, you may use shorter Map.of:
Map<String, Integer> priorityProgramTypes = Map.of(
"ACF", Integer.valueOf(2),
"VCX", Integer.valueOf(1),
"IFL", Integer.valueOf(3)
);
Use SortedMap interface and its implementation TreeMap:
SortedMap<Integer, List<String>> map = new TreeMap<>();
map.put(2, Collections.singletonList("ACF"));
map.put(1, Collections.singletonList("VCX"));
map.put(3, Collections.singletonList("IFL"));
The advantages are:
The keys are sorted, you can manage the order of them defining a Comparator in the constructor new TreeMap<>(comparator);. The order of processing from the sample above will be:
map.values().forEach(System.out::print);
// [VCX][ACF][IFL]
If more of the strings have the same priority, List<String> as the values of the map are more suitable.
Adding new priority (key) and value (List<String>) to the map will not break the sorted characteristics. For safe adding, I recommend Map::computeIfPresent .
I am pretty new to Java and i have a hard time solving this problem.
Lets say i have two Arraylists personIdList and titlelist which gets filled by a Database with the help of a ResultSet Object iteration.
personIdList.add(repPersonId.getLong("personID"));
titelList.add(repTitel.getString("titel"));
The first Arraylist(personIdList) contains Ids from different composer's like so :
[34, 34, 34, 37, 38, 133, 232, 232, 285, 285, 285, 285]
The second List(titlelist) contains Title's from that composer like so :
[Symphonie, Sinfonia Concertante, Oper, Symphonie, Ouverture zur Oper, Ouverture zur Oper, Konzert für zwei Klaviere, Sinfonie, Chöre aus der Schauspielmusik, Requiem, Klavierkonzert, Klavierkonzert]
Can i somehow establish a connection between those arrays? Because the composer id should be connectet to the corresponding Title.
For example(pseudo Code): personIdList.get(34) should give me all Titles that are connected to the Id 34.
Do i have to use ArrayLists or is there already something that does that?
As RC said. If you're not going to ORM Map it, you can iterate the id list and create a new Map (a key-value pair collection):
Map<Integer, List<String> personTitles = new HashMap<>();
for(Integer id: personIdList) {
// your solution for retrieving the titles from the database
// i would imagine you're using an entityManager.query or something which returns the result set of the titles
// where you have to pass the `id` as a parameter
// and get the list of titles and store them in a List<String>
personTitles.put(id, `your retrieved List<String>`);
}
And then you can just say personTitles.get(32) which should retrieve your list of titles.
If you can post more code of how you retrieve entries from the database, that would be helpful.
You can write your own collector which will create the desired map:
Map<Integer, List<String>> groupedIds = personIdList.stream()
.collect(new CustomCollector(titlelist.iterator()));
groupedIds.get(34); // [Symphonie, Sinfonia Concertante, Oper]
Bellow is the custom collector.
public class CustomCollector implements Collector<Integer, Map<Integer, List<String>>, Map<Integer, List<String>>> {
private Iterator<String> iterator;
public CustomCollector(Iterator<String> iterator) {
this.iterator = iterator;
}
#Override
public Supplier<Map<Integer, List<String>>> supplier() {
return HashMap::new;
}
#Override
public BiConsumer<Map<Integer, List<String>>, Integer> accumulator() {
return (map, id) -> {
List<String> list = map.get(id);
if(list == null) {
list = new ArrayList<>();
list.add(iterator.next());
map.put(id, list);
} else {
list.add(iterator.next());
}
};
}
#Override
public BinaryOperator<Map<Integer, List<String>>> combiner() {
return (m1, m2) -> m1;
}
#Override
public Function<Map<Integer, List<String>>, Map<Integer, List<String>>> finisher() {
return HashMap::new;
}
#Override
public Set<Characteristics> characteristics() {
return Collections.emptySet();
}
}
There is a good collection framework in Guava(provided by Google) library. If you are flexible to use external jars then use a multilistmap from guava library
ListMultimap<Integer, String> multimap = ArrayListMultimap.create();
multimap.put(repPersonId.getLong("personID"),(repTitel.getString("titel"));
So what it does is it accepts more than one value for one key and stores that in a list.
If you want the list of keys you can get them as Set by calling
Set<Integer> keyset=multimap.getKeySet();
While retrieving the values from the multimap it returns the list result
List<String> personTitles = multimap.get(personId);
This way you can retrieve the data so no need of creating a list and setting it to the map and this makes it easier to read than a map of array lists
If you even want all the values in your case titles you can get it by
Collection<String> titles=multimap.values();
Anyway collection can be casted to Set if you don't want duplicates or List if you want all values in mulimap irrespective of duplicates.
So I think this will be very handy and easily understandable compared to map of arraylists
Usually you should use a object and fill it.... normaly a ORM mapping or so..
But, if you want a homemade soluction, at least the id of the Arrays matches ? Like
arr1.get(1) is related to the arr2.get(1) ?
So I am very new to Java and as such I'm fighting my way through an exercise, converting one of my Python programs to Java.
I have run into an issue where I am trying to replicate the behavior, from python the following will return only the keys sorted (by values), not the values:
popular_numbers = sorted(number_dict, key = number_dict.get, reverse = True)
In Java, I have done a bit of research and have not yet found an easy enough sample for a n00b such as myself or a comparable method. I have found examples using Guava for sorting, but the sort appears to return a HashMap sorted by key.
In addition to the above, one of the other nice things about Python, that I have not found in Java is the ability to, easily, return a subset of the sorted values. In Python I can simply do the following:
print "Top 10 Numbers: %s" % popular_numbers[:10]
In this example, number_dict is a dictionary of key,value pairs where key represents numbers 1..100 and the value is the number of times the number (key) occurs:
for n in numbers:
if not n == '':
number_dict[n] += 1
The end result would be something like:
Top 10 Numbers: ['27', '11', '5', '8', '16', '25', '1', '24', '32',
'20']
To clarify, in Java I have successfully created a HashMap, I have successfully examined numbers and increased the values of the key,value pair. I am now stuck at the sort and return the top 10 numbers (keys) based on value.
Put the map's entrySet() into a List.
Sort this list using Collections.sort and a Comparator which sorts Entrys based on their values.
Use the subList(int, int) method of List to retrieve a new list containing the top 10 elements.
Yes, it will be much more verbose than Python :)
With Java 8+, to get the first 10 elements of a list of intergers:
list.stream().sorted().limit(10).collect(Collectors.toList());
To get the first 10 elements of a map's keys, that are integers:
map.keySet().stream().sorted().limit(10).collect(Collectors.toMap(Function.identity(), map::get));
HashMaps aren't ordered in Java, and so there isn't really a good way to order them short of a brute-force search through all the keys. Try using TreeMap: http://docs.oracle.com/javase/6/docs/api/java/util/TreeMap.html
Assuming your map is defined something like this and that you want to sort based on values:
HashMap<Integer, Integer> map= new HashMap<Integer, Integer>();
//add values
Collection<Integer> values= map.values();
ArrayList<Integer> list= new ArrayList<Integer>(values);
Collections.sort(list);
Now, print the first top 10 elements of the list.
for (int i=0; i<10; i++) {
System.out.println(list.get(i));
}
The values in the map are not actually sorted, because the HashMap is not sorted at all (it stores the values in the buckets based on the hashCode of the key). This code is just displaying 10 smallest elements in the map.
EDIT sort without loosing the key-value pairs:
//sorted tree map
TreeMap<Integer, Integer> tree= new TreeMap<>();
//iterate over a map
Iteartor<Integer> it= map.keySet().iterator();
while (it.hasNext()) {
Integer key= it.next();
tree.put(map.get(key), key);
}
Now you have the TreeMap tree that is sorted and has reversed key-value pairs from the original map, so you don't lose the information.
Try the next:
public static void main(String[] args) {
// Map for store the numbers
Map<Integer, Integer> map = new HashMap<Integer, Integer>();
// Populate the map ...
// Sort by the more popular number
Set<Entry<Integer, Integer>> set = map.entrySet();
List<Entry<Integer, Integer>> list = new ArrayList<>(set);
Collections.sort(list, new Comparator<Entry<Integer, Integer>>() {
#Override
public int compare(Entry<Integer, Integer> a,
Entry<Integer, Integer> b) {
return b.getValue() - a.getValue();
}
});
// Output the top 10 numbers
for (int i = 0; i < 10 && i < list.size(); i++) {
System.out.println(list.get(i));
}
}
Guava Multiset is a great fit for your use case, and would nicely replace your HashMap. It is a collection which counts the number of occurences of each element.
Multisets has a method copyHighestCountFirst, which returns an immutable Multiset ordered by count.
Now some code:
Multiset<Integer> counter = HashMultiset.create();
//add Integers
ImmutableMultiset<Integer> sortedCount = Multisets.copyHighestCountFirst(counter);
//iterate through sortedCount as needed
Use a SortedMap, call values(). The docs indicate the following:
The collection's iterator returns the values in ascending order of the corresponding keys
So as long as your comparator is written correctly you can just iterate over the first n keys
Build a list from the keyset.
Sort the HashMap by values using the keys to access the value in the Collection.sort() method.
Return a sub list of the sorted key set.
if you care about the values, you can use the keys in step 3 and build value set.
HashMap<String, Integer> hashMap = new HashMap<String, Integer>();
List list = new ArrayList(hashMap.keySet());
Collections.sort(list, (w1, w2) -> hashMap.get(w2) - hashMap.get(w1)); //sorted descending order by value;
return list.subList(0, 10);
To preserve the ranking order and efficiently return top count, much smaller than the size of the map size:
map.entrySet().stream()
.sorted(Map.Entry.comparingByValue(Comparator.reverseOrder()))
.limit(count)
.collect(toMap(Map.Entry::getKey, Map.Entry::getValue,
(e1, e2) -> e1,
LinkedHashMap::new))