Get minvalue of a Map(Key,Double) - java

Is there a method (maybe with Google Collections) to obtain the min value of a Map(Key, Double)?
In the traditional way, I would have to sort the map according to the values, and take the first/last one.

You can use the standard Collections#min() for this.
Map<String, Double> map = new HashMap<String, Double>();
map.put("1.1", 1.1);
map.put("0.1", 0.1);
map.put("2.1", 2.1);
Double min = Collections.min(map.values());
System.out.println(min); // 0.1
Update: since you need the key as well, well, I don't see ways in Collections or Google Collections2 API since a Map is not a Collection. The Maps#filterEntries() is also not really useful, since you only know the actual result at end of iteration.
Most straightforward solution would then be this:
Entry<String, Double> min = null;
for (Entry<String, Double> entry : map.entrySet()) {
if (min == null || min.getValue() > entry.getValue()) {
min = entry;
}
}
System.out.println(min.getKey()); // 0.1
(nullcheck on min left aside)

You still can use Collections.min with a custom Comparator to get the Map.Entry with the lower value:
Map<String, Double> map = new HashMap<String, Double>();
map.put("1.1", 1.1);
map.put("0.1", 0.1);
map.put("2.1", 2.1);
Map.Entry<String, Double> min = Collections.min(map.entrySet(), new Comparator<Map.Entry<String, Double>>() {
public int compare(Map.Entry<String, Double> entry1, Map.Entry<String, Double> entry2) {
return entry1.getValue().compareTo(entry2.getValue());
}
});
System.out.printf("%s: %f", min.getKey(), min.getValue()); // 0.1: 0.100000
With Java 8+:
Map.Entry<String, Double> min = Collections.min(map.entrySet(),
Map.Entry.comparingByValue());

Java8 One-Liner
Key key = Collections.min(map.entrySet(), Map.Entry.comparingByValue()).getKey()

In traditional way, I would have to
sort the map according to the values,
and take the first/last one. thanks
No, you wouldn't. You would have to iterate through all values and at each step compare the current element with the smallest one seen so far. That's O(n), compared with O(n*log(n)) for sorting - a potentially huge difference.
BTW, this is exactly how Collections.min() works.

Using Java 8 streams:
return map
.entrySet()
.stream()
.sorted(Comparator.comparingDouble(Map.Entry::getValue))
.findFirst()
.map(Map.Entry::getValue);
Or
return map
.entrySet()
.stream()
.min(Comparator.comparingDouble(Map.Entry::getValue))
.map(Map.Entry::getValue);
But if you want to do it multiple times, then definitely give heap a look.

I'd be inclined to use a Google Collections BiMap:
String minKey = HashBiMap.create(map).inverse().get(Collections.min(map.values()));
Or something like that (not tested).

Using java 8 (and static imports). We can make #superfav's solution much tidier:
Map<String, Double> myMap;
String theKeyWithHighestValue = Collections.min(myMap.entrySet(), comparingDouble(Entry::getValue)).getKey()

In Java 8 we can get easily:
Double minValue = map.entrySet().stream().min(Map.Entry.comparingByValue()).get().getValue();
Double maxValue = map.entrySet().stream().max(Map.Entry.comparingByValue()).get().getValue();

In order to do it efficiently, you may want to define your own data structure, such that it implements the Map interface,but also allows efficient getMin() operation.
This can be done using two internal data structures: a map and a tree (or heap data structure). Each time a new pair (K,V) is added, add them to the map, and also to the tree (as a single entry). This allows O(1) time for get(Key) operations, and O(log n) time for addition, removal, and getMin operations.

Related

How to get a Key from HashMap which corresponds to the max value?

I have the following TreeMap with the given 2 values:
Map<Integer, Integer> map = new TreeMap<>();
// 1 --> 3
// 2 --> 4
I want to get the key which has the max value. I get the max value via:
int max = map.values().stream().max(Integer::compare).get();
// 4
However, I cannot filter the map keys based on this max value. So, how can I get the key of the max value (2)? Or key of the given value in TreeMap? I used TreeMap instead of HashMap so that I can sort the map if needed (maybe not need).
I used TreeMap instead of HashMap so that I can sort the map if needed (maybe not need).
For that purpose, HashMap will suffice, you might replace TreeMap with a HashMap if you are not utilizing it for anything else. And moreover, TreeMap can't help with this task because maintains the order of entries based on keys, not on values (your example is slightly misleading - max value is mapped to a max key, if you change it, TreeMap will no longer be helpful).
To solve this problem with Stream API, firstly, you need to create a stream over the entry set, because you can't access a key when you have only a value.
Terminal operation max() returns an optional object that will hold entry (if result is present). Method map() invoked on an optional will transform Optional<Map.Entry<Integer, Integer>> into Optional<Integer>.
Method orElseThrow() in this case will be a better alternative to get(). Both will throw an exception if optional object will be empty. If according to your logic, value is guaranteed to be present it better specify explicitly with orElseThrow() that your intention is to throw an exception when result is not present, because this case is abnormal.
NavigableMap<Integer, Integer> map = new TreeMap<>();
int maxKey = map.entrySet().stream()
.max(Map.Entry.comparingByValue()) // Optional<Map.Entry<Integer, Integer>> - entry
.map(Map.Entry::getKey) // Optional<Integer> - key
.orElseThrow();
Since multiple keys could have the same value, it is possible that max value will be mapped to more than one key. In this case you might want to get a list of these keys:
NavigableMap<Integer, Integer> map = new TreeMap<>();
int maxValue = map.entrySet().stream()
.max(Map.Entry.comparingByValue())
.map(Map.Entry::getValue)
.orElseThrow();
List<Integer> maxValues = map.entrySet().stream()
.filter(entry -> entry.getValue() == maxValue)
.map(Map.Entry::getKey)
.collect(Collectors.toList());
Sidenote: when you are working with a TreeMap and don't expect that variable could be assigned with an unsorted implementation of the interface Map, then use interface NavigableMap as a type. It'll provide you access to such methods as getFirstEntry(), getFirstKey(), higherEntry(), etc. that will not be available with Map.
To get the proper key you can use this:
Optional<Map.Entry<Integer,Integer>> entry = map.entrySet().stream().max(Map.Entry.comparingByValue());
System.out.println(entry.get().getKey());
If you use the proper interface, this is extremely easy.
NavigableMap<Integer, Integer> map = new TreeMap<>();
return map.lastEntry().getKey();
More importantly, this is much more efficient than using any Stream at all.

Reverse order of LinkedHashMap to LinkedHashMap or ArrayList

I have a LinkedHashMap<String,String> which looks something like this (don't really know how to illustrate a HashMap):
{
"10/10/2010 10:10:10" => "SomeText1",
"10/10/2019 10:10:19" => "SomeText2",
"10/10/2020 10:10:20" => "SomeText3",
"10/10/2021 10:10:21" => "SomeText4"
}
And I want to put it like this:
{
"10/10/2021 10:10:21" => "SomeText4",
"10/10/2020 10:10:20" => "SomeText3",
"10/10/2019 10:10:19" => "SomeText2",
"10/10/2010 10:10:10" => "SomeText1"
}
I have written this solution which works because the result I want is an ArrayList, but i was thinking if there was an easier way to reverse the LinkedHashMap maintaining the same type using a tool like sort for example.
private LinkedHashMap<String, String> map = new LinkedHashMap<>();
int sizeOfHashMap = map.size();
ArrayList reversedHashToArrayList = new ArrayList(map.size());
for (Map.Entry<String,String> entry : map.entrySet()) {
String key = entry.getKey();
String value = entry.getValue();
reversedHashToArrayList.add(0,entry);
}
A LinkedHashMap orders by insertion; it would be more logical to sort on the associated date time:
private SortedMap<LocalDateTime, String> map = new TreeMap<>(Comparator.naturalOrder()
.reversed());
LocalDateTimeFormatter formatter = LocalDateTimeFormatter.ofPattern("MM/dd/uuuu HH:mm:ss");
map.put(LocalDateTime.parse("10/10/2010 10:10:10", formatter), "...");
To specify that the map is sorted, there is the interface SortedMap. Better use an interface, which is more versatile. The implementation class for a sorted map is the TreeMap. However you want a reversed comparison.
You could use a Local specific pattern. Mind that above I chose Month/Day and not the British Day/Month.
If your motive is just to reverse the map ( show in descending order ) you can use
Java.util.TreeMap.descendingMap() : It returns a reverse order view of the mappings contained in the map`
LinkedHashMap<String,String> map = .... //this is your intial hashmap
TreeMap<String,String> tmap = new TreeMap<>(map);
map.clear();
map.putAll(tmap.descendingMap());
This will do the trick.
Here Is My Own Written Logic for you. without using any built in functions to reverse:
LinkedHashMap<String, String> map = new LinkedHashMap<>();
map.put("10/10/2010 10:10:10", "SomeText1");
map.put("10/10/2019 10:10:19", "SomeText2");
map.put("10/10/2020 10:10:20", "SomeText3");
map.put("10/10/2021 10:10:21", "SomeText4");
LinkedHashMap<String, String> reversed = new LinkedHashMap<>();
String[] keys = map.keySet().toArray(new String[map.size()]);
for (int i = keys.length - 1; i >= 0; i--) {
reversed.put(keys[i], map.get(keys[i]));
}
If you want to keep using a LinkedHashMap reversing it while keeping it somewhat efficient is not as easy. This is a solution that reverses a given LinkedHashMap using the iterator order (which is predictable for a LinkedHashMap and therefore probably what you are looking for).
Note that other solutions like using a SortedMap or a TreeMap are probably still better. Yet, for the sake of sticking to your original question, here is a solution:
public static <K, V> LinkedHashMap<K, V> reverse(LinkedHashMap<K, V> map)
{
LinkedHashMap<K, V> reversedMap = new LinkedHashMap<K, V>();
ListIterator<Entry<K, V>> it = new ArrayList<>(map.entrySet()).listIterator(map.entrySet().size());
while (it.hasPrevious())
{
Entry<K, V> el = it.previous();
reversedMap.put(el.getKey(), el.getValue());
}
return reversedMap;
}
Note that you won't get around wrapping the entry set into an ArrayList sadly as only that provides you with an ListIterator which can be initialized to any point other than the first element. Having something like a reverseIterator() method would simplify life a lot - sadly there is none available.
Complexity wise you iterate the list twice with this approach, first for the listIterator call from start to the last element and then once more from the back to the front when using previous. So you are looking at O(2n) here.

Filtering a stream based on its values in a toMap collection

I have a situation where I have Player objects in a development project, and the task is simply measuring the distance and returning results which fall under a certain threshold. Of course, I'm wanting to use streams in the most concise manner possible.
Currently, I have a solution which maps the stream, and then filters via an iterator:
Stream<Player> str = /* source of my player stream I'm filtering */;
Map<Player, Double> dists = str.collect(Collectors.toMap(...)); //mapping function
Iterator<Map.Entry<Player, Double>> itr = map.entrySet().iterator();
while (itr.hasNext()) {
if (itr.next().getValue() <= radiusSquared) {
itr.remove();
}
}
However, what I'd like to achieve is something which performs this filtering while the stream is being operated upon, something which says "if this predicate fails, do not collect", to attempt and save the second iteration. Additionally, I don't want to calculate the distances twice, so doing a filter via the mapping function, and then re-mapping isn't a plausible solution.
The only real viable solution I've thought of is mapping to a Pair<A, B>, but if there's native support for some form of binary stream, that'd be better.
Is there native support for this in java's stream API?
Filtering a Map afterwards is not as bad as it seems, keep in mind that iterating over a Map does not imply the same cost as performing a lookup (e.g. hashing).
But instead of
Iterator<Map.Entry<Player, Double>> itr = map.entrySet().iterator();
while (itr.hasNext()) {
if (itr.next().getValue() <= radiusSquared) {
itr.remove();
}
}
you may simply use
map.values().removeIf(value -> value <= radiusSquared);
Even if you insist on having it as part of the collect operation, you can do it as postfix operation:
Map<Player, Double> dists = str.collect(
Collectors.collectingAndThen(Collectors.toMap(p->p, p->calculate(p)),
map -> { map.values().removeIf(value -> value <= radiusSquared); return map; }));
Avoiding to put unwanted entries in the first place is possible, but it implies manually retracing what the existing toMap collector does:
Map<Player, Double> dists = str.collect(
HashMap::new,
(m, p) -> { double value=calculate(p); if(value > radiusSquared) m.put(p, value); },
Map::putAll);
Note that your old-style iterator-loop could be rewritten in Java-8 using Collection.removeIf:
map.values().removeIf(dist -> dist <= radiusSquared);
So it does not actually that bad. Don't forget that keySet() and values() are modifiable.
If you want to solve this using single pipeline (for example, most of the entries are to be removed), then bad news for you. Seems that current Stream API does not allow you to do this without explicit use of the class with pair semantics. It's quite natural to create a Map.Entry instance, though already existing option is AbstractMap.SimpleEntry which has quite long and unpleasant name:
str.map(player -> new AbstractMap.SimpleEntry(player, getDistance(player)))
.filter(entry -> entry.getValue() > radiusSquared)
.toMap(Entry::getKey, Entry::getValue);
Note that it's likely that in Java-9 there will be Map.entry() static method, so you could use Map.entry(player, getDistance(player)). See JEP-269 for details.
As usual my StreamEx library has some syntactic sugar to solve this problem in cleaner way:
StreamEx.of(str).mapToEntry(player -> getDistance(player))
.filterValues(dist -> dist > radiusSquared)
.toMap();
And regarding the comments: yes, toMap() collector uses one-by-one insert, but don't worry: bulk inserts to map rarely improve the speed. You even cannot pre-size the hash-table (if your map is hash-based) as you don't know much about the elements being inserted. Probably you want to insert a million of objects with the same key: allocating the hash-table for million entries just to discover that you will have only one entry after insertion would be too wasteful.
If your goal is just to have one iteration and calculate distances only once, then you could do this:
Stream<Player> str = /* source of my player stream I'm filtering */;
Map<Player, Double> dists = new HashMap<>();
str.forEach(p -> {
double distance = /* calculate distance */;
if (distance <= radiusSquared) {
dists.put(p, distance);
}
});
No collector any more, but is it that important?

Java How to return top 10 items based on value in a HashMap

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))

Iterating through a LinkedHashMap in reverse order

I have a LinkedHashMap:
LinkedHashMap<String, RecordItemElement>
that I need to iterate through from a given key's position, backwards. So if I was given the 10th item's key, I'd need iterate backwards through the hashmap 9, 8, 7 etc.
The question requires a LinkedHashMap in reverse order, some answers suggesting using a TreeSet but this will reorder the map based upon the key.
This solution allows the iteration over the original LinkedHashMap not the new ArrayList as has also been proposed:
List<String> reverseOrderedKeys = new ArrayList<String>(linkedHashMap.keySet());
Collections.reverse(reverseOrderedKeys);
for (String key : reverseOrderedKeys) {
RecordItemElement line = linkedHashMap.get(key);
}
The HashMap:
HashMap<Integer, String> map = new HashMap<Integer, String>();
Reverse iterating over values:
ListIterator<Sprite> iterator = new ArrayList<String>(map.values()).listIterator(map.size());
while (iterator.hasPrevious()) String value = iterator.previous();
Reverse iterating over keys:
ListIterator<Integer> iterator = new ArrayList(map.keySet()).listIterator(map.size());
while (iterator.hasPrevious()) Integer key = iterator.previous();
Reverse iterating over both:
ListIterator<Map.Entry<Integer, String>> iterator = new ArrayList<Map.Entry<Integer, String>>(map.entrySet()).listIterator(map.size());
while (iterator.hasPrevious()) Map.Entry<Integer, String> entry = iterator.previous();
You don't have to iterate through it. But it would be handy to pull the keys off and store it in a list. Thats the only way you can do indexOf() type operations.
List<String> keyList = new ArrayList<String>(map.keySet());
// Given 10th element's key
String key = "aKey";
int idx = keyList.indexOf(key);
for ( int i = idx ; i >= 0 ; i-- )
System.out.println(map.get(keyList.get(i)));
new LinkedList(linkedHashMap.keySet()).descendingIterator();
Using "user22745008" solution and labdas with some generics you can have a very neat solution as a method:
public static <T, Q> LinkedHashMap<T, Q> reverseMap(LinkedHashMap<T, Q> toReverse)
{
LinkedHashMap<T, Q> reversedMap = new LinkedHashMap<>();
List<T> reverseOrderedKeys = new ArrayList<>(toReverse.keySet());
Collections.reverse(reverseOrderedKeys);
reverseOrderedKeys.forEach((key)->reversedMap.put(key,toReverse.get(key)));
return reversedMap;
}
This is an old question, but I think it's lacking an answer that takes a newer approach. The following uses Java 9 features:
Deque<Map.Entry<String, RecordItemElement>> top = map.entrySet().stream()
.takeWhile(e -> !givenKey.equals(e.getKey()))
.collect(Collectors.toCollection(ArrayDeque::new));
The code above streams the map's entryset, keeping entries until a key equal to the given key is found. Then, the entries are collected to an ArrayDeque.
One detail is missing, though. Depending on whether you need the entry that matches the given key to also be included in the result or not, you might need to manually add it to the deque. If you don't want it added, then you're done. Otherwise, simply do:
top.add(Map.entry(givenKey, map.get(givenKey)));
Now, to iterate the Deque in reverse order, simply use its descendingIterator():
Iterator<Map.Entry<String, RecordItemElement>> descIt = top.descendingIterator();
It's worth mentioning that this approach only works if the stream is sequential. Anyways, we wouldn't have gained anything using a parallel stream here.

Categories