Sort HashMap by Value in descending order - java

I have a HashMap with Document as Key and a Double Value as Value.
My aim is to sort the Hashmap by descending value. The .reverse() should be after comparingbyValue() but conflicts with sorted. How do I solve this?
HashMap<Document, Double> sortedMap = Map.entrySet().stream().sorted(Entry.comparingByValue())
.collect(Collectors.toMap(Entry::getKey, Entry::getValue, (e1, e2) -> e1, LinkedHashMap::new));

HashMap is fundamentally unsorted. TreeMap is fundamentally sorted on key. In other words, you can't do this. Just now how it works. You have two options:
Figure out a way to sort on keys
And then just use a TreeMap with a custom comparator.
Copy the whole thing
Make a tuple class, then copy the entire thing into an ArrayList by turning each k/v pair into a tuple, then sort that list on value as you want, then move the entire thing into a LinkedHashMap which preserves order. You cannot now modify the thing without going through this entire routine again.
Rethink your architecture
If neither is acceptable you'll have to go back to the drawing board.

Related

Java Sorting a Map using Steams VS TreeMap

Consider the following Java HashMap.
Map<String, String> unsortMap = new HashMap<String, String>();
unsortMap.put("Z", "z");
unsortMap.put("B", "b");
unsortMap.put("A", "a");
unsortMap.put("C", "c");
Now I wish to sort this Map by Key. One option is for me to use a TreeMap for this purpose.
Map<String, String> treeMap = new TreeMap<String, String>(unsortMap);
Another option is for me to Use Java Streams with Sorted(), as follows.
Map<String, Integer> sortedMap = new HashMap<>();
unsortMap.entrySet()
.stream()
.sorted(Map.Entry.comparingByKey())
.forEachOrdered(x -> sortedMap.put(x.getKey(), x.getValue()));
Out of these two, which option is preferred and why (may be in terms of performance)?
Thank you
As pointed out by others dumping the sorted stream of entries into a regular HashMap would do nothing... LinkedHashMap is the logical choice.
However, an alternative to the approaches above is to make full use of the Stream Collectors API.
Collectors has a toMap method that allows you to provide an alternative implementation for the Map. So instead of a HashMap you can ask for a LinkedHashMap like so:
unsortedMap.entrySet()
.stream()
.sorted(Map.Entry.comparingByKey())
.collect(Collectors.toMap(
Map.Entry::getKey,
Map.Entry::getValue,
(v1, v2) -> v1, // you will never merge though ask keys are unique.
LinkedHashMap::new
));
Between using a TreeMap vs LinkedHashMap ... The complexity of construction is likely to be the same something like O(n log n)... Obviously the TreeMap solution is a better approach if you plan to keep adding more elements to it... I guess you should had started with a TreeMap in that case. The LinkedHashMap option has the advantage that lookup is going to be O(1) on the Linked or the original unsorted map whereas as TreeMap's is something like O(log n) so if you would need to keep the unsorted map around for efficient lookup whereas in if you build the LinkedHashMap you could toss the original unsorted map (thus saving some memory).
To make things a bit more efficient with LinkedHashMap you should provide an good estimator of the required size at construction so that there is not need for dynamic resizing, so instead of LinkedHashMap::new you say () -> new LinkedHashMap<>(unsortedMap.size()).
I'm my opinion the use of a TreeMap is more neat... as keeps the code smaller so unless there is actual performance issue that could be addressed using the unsorted and sorted linked map approach I would use the Tree.
Your stream code won't even sort the map, because it is performing the operation against a HashMap, which is inherently unsorted. To make your second stream example work, you may use LinkedHashMap, which maintains insertion order:
Map<String, Integer> sortedMap = new LinkedHashMap<>();
unsortMap.entrySet()
.stream()
.sorted(Map.Entry.comparingByKey())
.forEachOrdered(x -> sortedMap.put(x.getKey(), x.getValue()));
But now your two examples are not even the same underlying data structure. A TreeMap is backed by a tree (red black if I recall correctly). You would use a TreeMap if you wanted to be able to iterate in a sorted way, or search quickly for a key. A LinkedHashMap is hashmap with a linked list running through it. You would use this if you needed to maintain insertion order, for example when implementing a queue.
The second way does not work, when you call HashMap#put, it does not hold the put order. You might need LinkedHashMap.
TreeMap v.s. Stream(LinkedHashMap):
code style. Using TreeMap is more cleaner since you can achieve it in one line.
space complexity. If the original map is HashMap, with both method you need to create a new Map. If If the original map is LinkedHashMap, then you only need create a new Map with the first approach. You can re-use the LinkedHashMap with the second approach.
time complexity. They should both have O(nln(n)).

Sorted Iteration of HashMap Java

I know that HashMap is not sorted but is there any away i can create iterator which returns values in sorted order of key. I can use Sorted versions of the collections but I am looking for a way to do the same with Hash Based map.
Any such iterator would have to internally sort all the keys of the HashMap in order to be able to iterate over them in sorted order. It would be more efficient to use an already sorted Map implementation.
You can use TreeMap since its a sorted map.
With Java 8 this is very simple:
import static java.util.Map.Entry.comparingByKey;
public <K extends Comparable<? super K>, V> Iterator<V> orderedIterator(final Map<K, V> map) {
return map.entrySet().stream()
.sorted(comparingByKey())
.map(Map.Entry::getValue)
.iterator();
}
Note, this is slow, as the Stream needs to be sorted each time - so iteration becomes O(n lg n) rather than O(n). If you need to do this a lot, you would be better off using a TreeMap - where insertion is O(lg n) (rather than O(1)) but iteration is still O(n).
I'm not sure this is completely possible, at least from a map point of view, although we could create a special hash map the returns keys from a sort order.
The map could extend HashMap and have a variable the contains the sort order and then have a method that returns the keys and values in the sort order.
You could have a static utility method that takes a HashMap and returns an array of Map.Entrys in the sort order.
While the above may work, TreeMap is probably the way to go. It's designed for this task and was written by Josh Blotch so it's bound to be fast at what it does. Reinventing the wheel always takes longer and doesn't work as well.
Note: This depends on the use case. If you only need to use the sorted values once, then the utility method or custom HashMap implementation will be best. If you're planning on using the Map often, then go with a TreeMap.

How to get a specific key or value from a ConcurrentHashMap

I honestly tried to look at a lot of posts but I am not sure what is the correct way of getting a specific key from a ConcurrentHashMap.
How to get the first key in a ConcurrentHashMap?
How to get the key on first encounter of a certain value?
How to update the value of a specific key?
Okay, lets tackle these questions one by one:
1) You cannot. A ConcurrentHashMap has no order. There is no "first" and no "last".
2) A ConcurrentHashMap provides an entrySet which is "weakly consistent", so that if the content of the Map changes during iteration you might see the changes, you might not:
public static <K, V> Optional<K> getKeyForVal(final V val, final Map<K, V> map) {
return map.entrySet().stream()
.filter(e -> e.getValue().equals(val))
.map(Map.Entry::getKey)
.findFirst();
}
Again, there is no "first" this is just the first one encountered during iteration.
3) This is easy, just add the same key -> value pair again, this will update the value for a particular key.
So the main takeaway here is that a ConcurrentHashMap has no order. The order of iteration over a ConcurrentHashMap is undefined and may even change between different iterations. There is no "first", no "last".
Something like ConcurrentSkipListMap does have an ordering, it's sorted by the order of the keys as defined by a Comparator. So that would have a "first" and a "last" element.

ArrayList to HashMaps in Java

I am trying to get values from an ArrayList that is sorted and want to store it in a HashMap, where the values of the ArrayList become keys of the HashMap. Will the order of the values in the HashMap still be the same as that of ArrayList?
No. Use a TreeMap instead. This will preserve the order of insertion.
HashMap makes no guarantees as to the order the mappings are stored or iterated, so simply running through the ArrayList and putting them into the HashMap as keys will very likely result in unordered iterations.
As others have pointed out, LinkedHashMap does preserve insertion order for iterations. An additional run of insertions will result in unordered iterations again, though. Both HashMap and LinkedHashMap support constant time lookup - LinkedHashMap pays for its extra feature in space (by maintaining pointers between the keys).
As others have also pointed out, TreeMap preserves order after updates, so this might be a better option for you, or not. Of course, if the ArrayList is sorted with a specific Comparator, you must feed that same Comparator to the TreeMap on construction for the sorting to be the same. Note that TreeMap does not have constant time lookup, due to being implemented as a Red-Black search tree.
As your ArrayList has been ordered, no need to use a TreeMap because this will compare to order again and it's not necessary. You should use a LinkedHashMap that will keep the exact order of your ArrayList when you put your value in.
Check This: Insert Values of ArrayList into HashMap
HashMap<String, Item> itemMap = new HashMap<String, Item>();
for (Item item : itemList)
{
itemMap.put(item.getitemCode(), item);
}

How to sort HashMap of directories depth first?

I created a hashmap where I store directories as keys and then their contents as values.
When I iterate over the hash map and print everything out I get
files/abknl/bbxudleuf/jlffhq/y/xwjj/ell/ek.java
files/abknl/bbxudleuf/jlffhq/y/xwjj/ell/nu.java
files/abknl/bbxudleuf/jlffhq/y/xwjj/ell/os.java
files/abknl/bbxudleuf/jlffhq/y/xwjj/ell/njwqdp/di.html
files/abknl/bbxudleuf/jlffhq/y/xwjj/ell/njwqdp/po.html
What I want to get however is
files/abknl/bbxudleuf/jlffhq/y/xwjj/ell/njwqdp/di.html
files/abknl/bbxudleuf/jlffhq/y/xwjj/ell/njwqdp/po.html
files/abknl/bbxudleuf/jlffhq/y/xwjj/ell/ek.java
files/abknl/bbxudleuf/jlffhq/y/xwjj/ell/nu.java
files/abknl/bbxudleuf/jlffhq/y/xwjj/ell/os.java
Any ideas?
To come to this I am sorting the hashmaps keys and then print the values of each key
A HashMap is not usually sorted. You will want to use a SortedMap, for example TreeMap. This class will let you use your own Comparator, and thus lets you sort its contents anyway you want it. From the TreeMap(Comparator<? super K>) constructor Javadoc:
Constructs a new, empty tree map, ordered according to the given comparator.
Try using a TreeMap with a suitable Comparator, then putAll entries you have in your HashMap.
If you need to start with a HashMap, you can create a new TreeMap(myHashMap). This will have the same mappings, but will iterate over them in sorted order.

Categories

Resources