Get 3 highest values from Map<String,String> - java

I have a Map<String,String> which has entries like "User1","43". Now I want a "Top 3" of the highest values.
It would be easier with a Map<String,Integer>, but due to technical limitations I can just grab the Map as a <String,String>.
What's the most efficient way to convert a <String,String> map to a <String,Int> one and then sort it?

To convert from <String, String> to <String, Integer> you can use:
Map<String, Integer> treemap = new HashMap<String, Integer>();
for (Entry<String, String> entry : entries) {
treemap.put(entry.getKey(), Integer.parseInt(entry.getValue()));
}
However, then you will have to iterate the Map again. If you don't need the whole map, but rather just the top 3, then you can simply iterate the entries and get the top three by comparison.
Or you can reverse the key and value and use a TreeMap<Integer, String> with a Comparator, if you need both the top elements and the whole data.

There are a few ways:
Create SortedMap, e.g. TreeMap with a custom -anonymous- Comparator which performs comparisons by looking up the keys it gets in the compare() method call against the values in the original map.
Populate it with all key/value entries in the original through addAll() method.
Watch the map being sorted by value.
Grab the head/tail (depending on how your comparator sorts)
Similar to above:
Create a TreeSet of keys with a custom comparator as above...
Populate it with the keySet() of your original map.
Grab the head/tail set of the keys.
Create a new Map from those keys and value from the original map...

You could just put the values in a List and sort it:
ArrayList<Integer> highest = new ArrayList<Integer>();
for (String value : map.values()) {
highest.add(Integer.parseInt(value));
}
Collections.sort(highest);
for(int i = highest.size() - 1; i >=0 && i > highest.size()-4; i--){
System.out.println(highest.get(i));
}
If the map is very large it might be better to iterate through it and only select the 3 highest values without sorting the whole list.

You could iterate through the values of the Map (with Map.values()), converting each to an Integer (with Integer.getInteger(String s)), and keeping track of the top 3 you see.
Or, you could do as above but instead of keeping track of the top 3, make a LinkedList and insert each Integer at the correct place (traverse the LinkedList until you find where the Integer should be inserted).

Related

How to search efficiently return all the values for keys which are in a arraylist

I am having an arraylist which contains a list of numbers. I want to get all the values from the HashMap which has the keys which are in the array list.
For example say the array list contains 1,2,3,4,5,6,7,8,9 list
I want to get all the values for the keys 1,2,3,4,5,6,7,8,9 map
So currently I am implementing
for (i=0;i<list.size;i++){
map_new.put(list.get(),map.get(list.get()))
}
Is there any efficient way to do this?
Your code basically assumes that map.get(list.get()) always returns a value, you can try the following code which first filters the not null values from the list object and then adds to the new Map:
Map<String, Integer> newMap = list.stream().
filter(key -> (map.get(key) != null)).//filter values not present in Map
collect(Collectors.toMap(t -> t, t -> map.get(t)));//now collect to a new Map
In case, if map.get(list.get()) returns null, your code creates a Map with null values in it for which you might end up doing null checks, which is not good, rather you can ensure that your newly created Map always contains a value for each key.
Assuming the signature of list and the map are as following
List<Integer> list;
Map<Integer, Integer> map;
You can use following
for(int a : list){
Integer b = map.get(a);
if(b != null)
// b is your desired value you can store in another collection
}
Which is similar to the procedure you have already used.
As you can access the map in O(1) so the complexity of this code will be O(listsize)
There is not much you can do for efficiency. Still couple of small things you can do considering code example you have given above:
1) Change your for loop to
for(Long num : list)
instead of iterating using index, this will reduce you get calls over list.
2) You can update the existing map , so that you even do not need to iterate.
map.keySet().retainAll(list);
for(Long key: map.keySet()) {
System.out.println(map.get(key));
}
With this existing map will contain only those data whose keys are present in list, but you should use it carefully depending upon rest of the code logic.
You can capitalize on the fact that the keyset of a map is backed by the map itself and modifications to the keyset will reflect back to the map itself. This way, you can use the retainAll() method of the Set interface to reduce the map with a single line of code. Here is an example:
final Map<Integer, String> m = new HashMap<Integer, String>();
m.put(1, "A");
m.put(2, "B");
m.put(3, "C");
m.put(4, "D");
m.put(5, "E");
final List<Integer> al = Arrays.asList(new Integer[] { 2, 4, 5 });
System.out.println(m);
m.keySet().retainAll(al);
System.out.println(m);
This will output:
{1=A, 2=B, 3=C, 4=D, 5=E}
{2=B, 4=D, 5=E}

How to sort two HashMap which has common key?

I have two HashMap which look like this,
public HashMap<Integer, String> scoreName = new HashMap<Integer, String>();
public HashMap<Integer, Integer> scoreValue = new HashMap<Integer, Integer>();
Here both HashMap has common key. I want to sort scoreValue HashMap and scoreName HashMap where scoreName HashMap should also be sorted according scoreValue.
Example:
scoreValue = 5,1,7,7,9
scoreName = a, b , c, d,e
after sorting scoreValue = 1,5,7,7,9
sorting scoreName = b,a,c,d,e
I can sort my scoreValue
List sortedKeys = new ArrayList(scoreValue.values());
Collections.sort(sortedKeys);
but this is not the solution that I am looking for !
HashMaps are unordered collections, so you cannot sort them.
What would be better here is to have a Score object which contains the key, name and value as member variables.
Then just have a simple List<Score> and then call Collections.sort() on the list.
If you need the fast id look-up as well then you can have as HashMap of id to Score. Iterate over the list when you need them in order. Look them up in the map to find the score and name for a given id.
If each Score object wants to know it's ranking in the list then you can loop through the sorted collection after doing the sort and have a variable in the Score object to store that as well.
TreeMap/TreeSet might also be helpful in this case but they don't allow random access (i.e. who is at position 3) and TreeMap sorts based on the key in the map, not the value.
HashMap has no ordering of any sort. You can use a LinkedHashMap or you can use trees (or data structures similar to trees). Using a LinkedHashMap is very similar to using a HashMap.

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

Accessing a Map via index?

Is it tossibe to aceess a Map<Integer, Integer> via index?
I need to get the second element of the map.
You're using the wrong data structure. If you need to lookup by key, you use a Map. If you need to lookup by index or insertion order, use something that lets you index, like an array or list or linked list.
If you need to lookup by both, then you need to create a composite data structure that tracks both keys and insertion order (implementation would be backed by a Map and one of the above aforementioned data structures).
There's even one built into the framework: LinkedHashMap.
There is no direct way to access a map "via index", but it looks like you want a LinkedHashMap, which provides a predictable iteration order:
... which is normally the order in which keys were inserted into the map (insertion-order). Note that insertion order is not affected if a key is re-inserted into the map. (A key k is reinserted into a map m if m.put(k, v) is invoked when m.containsKey(k) would return true immediately prior to the invocation.)
A definition of index is not applicable to Map, as it's not an ordered collection by default.
You can use a TreeMap, which implements NavigableMap, and then iterate the key set using the navigableKeySet() method.
If you just need to get the second element all the time. Why not use a iterator and then do next ,next.
It will depends of Map implementation, but if you want to retrieve the second inserted element, you can use a LinkedHashMap and then create an iterator on values.
Map<Integer, Integer> map = new LinkedHashMap<Integer, Integer>();
map.put(1, 1);
map.put(2, 2);
Integer value = null;
if (map.size() > 1) {
Iterator<Integer> iterator = map.values().iterator();
for (int i = 0; i < 2; i++) {
value = iterator.next();
}
}
// value contains second element
System.out.println(value);
Map does not store elements in the insertion order. It stores elements into buckets based on the value of the hashCode of the element that is being stored. So no, you cannot get it by index.
Anyways, you could imitate something like this by using the LinkedHashMap implementation of the Map interface, which remembers the insertion order (unlinke the HashMap).
You would have to "hack" with manual index counter and the code would look something like this:
Map<String, String> map= new LinkedHashMap<>();
map.put("1", "one");
map.put("2", "two");
map.put("3", "three");
int index= 0;
for (String key : map.keySet()) {
if (index++ == 1) {
System.out.println(map.get(key));
}
}
Will print:
"two"
Which is what you want.
You can also use org.apache.commons.collections.map.ListOrderedMap from apache commons-collection. It implements Map and provides some methods from the List interface, like get(int index) and remove(int index).
It uses an ArrayList internally, so performance will be better than iterating on a Map to retrieve a value at specified position.
Not sure if this is any "cleaner", but:
If use LinkedHashMap and u want to retrieve element inserted second following will work
List keys = new ArrayList(map.keySet());
Object obj = map.get(keys.get(1));
//do you staff here

How can I sort the keys of a Map in Java?

This is a very basic question, I'm just not that good with Java. I have a Map and I want to get a list or something of the keys in sorted order so I can iterate over them.
Use a TreeMap, which is an implementation of the SortedMap interface. It presents its keys in sorted order.
Map<String, Object> map = new TreeMap<String, Object>();
/* Add entries to the map in any order. */
...
/* Now, iterate over the map's contents, sorted by key. */
for (Map.Entry<String, ?> entry : map.entrySet()) {
System.out.println(entry.getKey() + ": " + entry.getValue());
}
If you are working with another Map implementation that isn't sorted as you like, you can pass it to the constructor of TreeMap to create a new map with sorted keys.
void process(Map<String, Object> original) {
Map<String, Object> copy = new TreeMap<String, Object>(original);
/* Now use "copy", which will have keys in sorted order. */
...
}
A TreeMap works with any type of key that implements the Comparable interface, putting them in their "natural" order. For keys that aren't Comparable, or whose natural ordering isn't what you need, you can implement your own Comparator and specify that in the constructor.
You have several options. Listed in order of preference:
Use a SortedMap:
SortedMap<whatever> myNewMap = new TreeMap<whatever>(myOldMap);
This is vastly preferable if you want to iterate more than once. It keeps the keys sorted so you don't have to sort them before iterating.
There is no #2.
There is no #3, either.
SortedSet<whatever> keys = new TreeSet<whatever>(myMap.keySet());
List<whatever> keys = new ArrayList<whatever>(myMap.keySet());
Collections.sort(keys);
The last two will get you what you want, but should only be used if you only want to iterate once and then forget the whole thing.
You can create a sorted collection when iterating but it make more sense to have a sorted map in the first place. (As has already been suggested)
All the same, here is how you do it.
Map<String, Object> map;
for(String key: new TreeSet<String>(map.keySet()) {
// accessed in sorted order.
}
Apart from the methods mentioned in other answers, with Java 8 streams, another shorthand to get a sorted key list from a map would be -
List<T> sortedKeys = myMap.keySet().stream().sorted().collect(Collectors.toList());
One could actually get stuff done after .sorted() as well (like using a .map(...) or a .forEach(...)), instead of collecting it in the list and then iterating over the list.

Categories