Sort map using comparator not working properly - java

I'm having a map(this comes directly from the web service, so the input is map anyways) and I'm trying to sort the map based on description. But, two keys are having empty description and they are required anyhow now. When I sort one key goes on missing and one key with empty description is available. Here is my code
public class TestArabic {
public static void main(String[] args) {
Map<Integer, NationalityDto> m = new HashMap<Integer, NationalityDto>();
NationalityDto n10 = new NationalityDto();
n10.setNatid(110);
n10.setDesc("");
m.put(110, n10);
NationalityDto n2 = new NationalityDto();
n2.setNatid(102);
n2.setDesc("الهند");
m.put(102, n2);
NationalityDto n3 = new NationalityDto();
n3.setNatid(103);
n3.setDesc("سعودي");
m.put(103, n3);
NationalityDto n1 = new NationalityDto();
n1.setNatid(101);
n1.setDesc("مصر");
m.put(101, n1);
NationalityDto n4 = new NationalityDto();
n4.setNatid(104);
n4.setDesc("الكويت");
m.put(104, n4);
NationalityDto n5 = new NationalityDto();
n5.setNatid(105);
n5.setDesc("لبنان");
m.put(105, n5);
NationalityDto n6 = new NationalityDto();
n6.setNatid(106);
n6.setDesc("");
m.put(106, n6);
System.out.println("Actual map");
System.out.println(m);
Map<Integer, NationalityDto> sortedMap = sortByValue(m);
System.out.println("About to sort the map");
System.out.println(sortedMap);
List<NationalityDto> list = new ArrayList<>();
//Add elements
for(Map.Entry<Integer, NationalityDto> m1 : sortedMap.entrySet()) {
list.add(m1.getValue());
}
Collections.sort(list, (e1, e2) -> e1.getDesc().compareTo(e2.getDesc()));
Map<Integer, NationalityDto> map = new LinkedHashMap<>();
for(NationalityDto dto : list){
map.put(dto.getNatid(), dto);
}
System.out.println("Sorted map");
System.out.println(map);
}
private static Map<Integer, NationalityDto> sortByValue(Map m) {
Map<Integer, NationalityDto> sortedMap = new TreeMap(new ValueComparator(m));
sortedMap.putAll(m);
return sortedMap;
}
}
class ValueComparator implements Comparator<Integer> {
Map<Integer, NationalityDto> map;
public ValueComparator(Map map) {
this.map = map;
}
public int compare(Integer s1, Integer s2) {
// TODO Auto-generated method stub
return ((NationalityDto) map.get(s1)).getDesc().compareTo(((NationalityDto) map.get(s2)).getDesc());
}
}
Output
{101=NationalityDto [natid=101, desc=مصر], 102=NationalityDto [natid=102, desc=الهند], 103=NationalityDto [natid=103, desc=سعودي], 104=NationalityDto [natid=104, desc=الكويت], 105=NationalityDto [natid=105, desc=لبنان], 106=NationalityDto [natid=106, desc=], 110=NationalityDto [natid=110, desc=]}
About to sort the map
{106=NationalityDto [natid=110, desc=], 104=NationalityDto [natid=104, desc=الكويت], 102=NationalityDto [natid=102, desc=الهند], 103=NationalityDto [natid=103, desc=سعودي], 105=NationalityDto [natid=105, desc=لبنان], 101=NationalityDto [natid=101, desc=مصر]}
Sorted map
{110=NationalityDto [natid=110, desc=], 104=NationalityDto [natid=104, desc=الكويت], 102=NationalityDto [natid=102, desc=الهند], 103=NationalityDto [natid=103, desc=سعودي], 105=NationalityDto [natid=105, desc=لبنان], 101=NationalityDto [natid=101, desc=مصر]}
Now 106 key is missing.

You cannot sort a TreeMap by value. Anyone who told you you could was wrong, and the value comparator you have used is broken.
The only valid way of sorting a map by value is the one you've used in your main method: sort the entries as a list and dump into a LinkedHashMap.
for(Map.Entry<Integer, NationalityDto> m1 : sortedMap.entrySet()) {
list.add(m1.getValue());
}
Collections.sort(list, (e1, e2) -> e1.getDesc().compareTo(e2.getDesc()));
Map<Integer, NationalityDto> map = new LinkedHashMap<>();
for(NationalityDto dto : list){
map.put(dto.getNatid(), dto);
This part will sort the entries if you don't give it a broken map in the first place. Your sortByValue method is broken; don't use it.

Related

sorting hashmap with Collections.sort restarts the sort once every few values [duplicate]

This question already has answers here:
Sort a Map<Key, Value> by values
(64 answers)
Closed 3 years ago.
How are we able to sort a HashMap<key, ArrayList>?
I want to sort on the basis of a value in the ArrayList.
Do you have to use a HashMap? If you only need the Map Interface use a TreeMap
If you want to sort by comparing values in the HashMap. You have to write code to do this, if you want to do it once you can sort the values of your HashMap:
Map<String, Person> people = new HashMap<>();
Person jim = new Person("Jim", 25);
Person scott = new Person("Scott", 28);
Person anna = new Person("Anna", 23);
people.put(jim.getName(), jim);
people.put(scott.getName(), scott);
people.put(anna.getName(), anna);
// not yet sorted
List<Person> peopleByAge = new ArrayList<>(people.values());
Collections.sort(peopleByAge, Comparator.comparing(Person::getAge));
for (Person p : peopleByAge) {
System.out.println(p.getName() + "\t" + p.getAge());
}
If you want to access this sorted list often, then you could insert your elements into a HashMap<TreeSet<Person>>, though the semantics of sets and lists are a bit different.
Sorted List by hasmap keys:
SortedSet<String> keys = new TreeSet<String>(myHashMap.keySet());
Sorted List by hashmap values:
SortedSet<String> values = new TreeSet<String>(myHashMap.values());
In case of duplicated map values:
List<String> mapValues = new ArrayList<String>(myHashMap.values());
Collections.sort(mapValues);
Good Luck!
http://snipplr.com/view/2789/sorting-map-keys-by-comparing-its-values/
get the keys
List keys = new ArrayList(yourMap.keySet());
Sort them
Collections.sort(keys)
print them.
In any case, you can't have sorted values in HashMap (according to API This class makes no guarantees as to the order of the map; in particular, it does not guarantee that the order will remain constant over time ].
Though you can push all these values to LinkedHashMap, for later use as well.
Seems like you might want a treemap.
http://docs.oracle.com/javase/7/docs/api/java/util/TreeMap.html
You can pass in a custom comparator to it if that applies.
In Java 8:
Comparator<Entry<String, Item>> valueComparator =
(e1, e2) -> e1.getValue().getField().compareTo(e2.getValue().getField());
Map<String, Item> sortedMap =
unsortedMap.entrySet().stream().
sorted(valueComparator).
collect(Collectors.toMap(Entry::getKey, Entry::getValue,
(e1, e2) -> e1, LinkedHashMap::new));
Using Guava:
Map<String, Item> map = ...;
Function<Item, Integer> getField = new Function<Item, Integer>() {
public Integer apply(Item item) {
return item.getField(); // the field to sort on
}
};
comparatorFunction = Functions.compose(getField, Functions.forMap(map));
comparator = Ordering.natural().onResultOf(comparatorFunction);
Map<String, Item> sortedMap = ImmutableSortedMap.copyOf(map, comparator);
Custom compare function which includes functionality for the Turkish alphabet or other different languages than english.
public <K extends Comparable,V extends Comparable> LinkedHashMap<K,V> sortByKeys(LinkedHashMap<K,V> map){
List<K> keys = new LinkedList<K>(map.keySet());
Collections.sort(keys, (Comparator<? super K>) new Comparator<String>() {
#Override
public int compare(String first, String second) {
Collator collator = Collator.getInstance(Locale.getDefault());
//Collator collator = Collator.getInstance(new Locale("tr", "TR"));
return collator.compare(first, second);
}
});
LinkedHashMap<K,V> sortedMap = new LinkedHashMap<K,V>();
for(K key: keys){
sortedMap.put(key, map.get(key));
}
return sortedMap;
}
here is the using example as the following
LinkedHashMap<String, Boolean> ligList = new LinkedHashMap<String, Boolean>();
ligList = sortByKeys(ligList);
Without any more information, it's hard to know exactly what you want. However, when choosing what data structure to use, you need to take into account what you need it for. Hashmaps are not designed for sorting - they are designed for easy retrieval. So in your case, you'd probably have to extract each element from the hashmap, and put them into a data structure more conducive to sorting, such as a heap or a set, and then sort them there.
If you want to combine a Map for efficient retrieval with a SortedMap, you may use the ConcurrentSkipListMap.
Of course, you need the key to be the value used for sorting.
have you considered using a LinkedHashMap<>()..?
public static void main(String[] args) {
Map<Object, Object> handler = new LinkedHashMap<Object, Object>();
handler.put("item", "Value");
handler.put(2, "Movies");
handler.put("isAlive", true);
for (Map.Entry<Object, Object> entrY : handler.entrySet())
System.out.println(entrY.getKey() + ">>" + entrY.getValue());
List<Map.Entry<String, Integer>> entries = new ArrayList<Map.Entry<String, Integer>>();
Collections.sort(entries, new Comparator<Map.Entry<String, Integer>>() {
public int compare(Map.Entry<String, Integer> a,
Map.Entry<String, Integer> b) {
return a.getValue().compareTo(b.getValue());
}
});
}
results into an organized linked object.
item>>Value
2>>Movies
isAlive>>true
check the sorting part picked from here..
I have developed a class which can be used to sort a map on the basis of keys and values. The basic idea is if you have sort a map using keys then create a TreepMap from your Map which will sort the map by keys. And in case of sorting by values create a list from entrySet and sort the list using comparator interface.
Here is the full solution :
public static void main(String[] args) {
Map<String, Integer> unSortedMap = new LinkedHashMap<String, Integer>();
unSortedMap.put("A", 2);
unSortedMap.put("V", 1);
unSortedMap.put("G", 5);
System.out.println("Unsorted Map :\n");
for (Map.Entry<String, Integer> entry : unSortedMap.entrySet()) {
System.out.println(entry.getKey() + " " + entry.getValue());
}
System.out.println("\n");
System.out.println("Sorting Map Based on Keys :\n");
Map<String, Integer> keySortedMap = new TreeMap<String, Integer>(unSortedMap);
for (Map.Entry<String, Integer> entry : keySortedMap.entrySet()) {
System.out.println(entry.getKey() + " " + entry.getValue());
}
System.out.println("\n");
System.out.println("Sorting Map Based on Values :\n");
List<Entry<String, Integer>> entryList = new ArrayList<Entry<String, Integer>>(unSortedMap.entrySet());
Collections.sort(entryList, new Comparator<Entry<String, Integer>>() {
#Override
public int compare(Entry<String, Integer> obj1, Entry<String, Integer> obj2) {
return obj1.getValue().compareTo(obj2.getValue());
}
});
unSortedMap.clear();
for (Entry<String, Integer> entry : entryList) {
unSortedMap.put(entry.getKey(), entry.getValue());
System.out.println(entry.getKey() + " " + entry.getValue());
}
}
Code is properly tested :D
Sorting HashMap by Value:
As others have pointed out. HashMaps are for easy lookups if you change that or try to sort inside the map itself you will no longer have O(1) lookup.
The code for your sorting is as follows:
class Obj implements Comparable<Obj>{
String key;
ArrayList<Integer> val;
Obj(String key, ArrayList<Integer> val)
{
this.key=key;
this.val=val;
}
public int compareTo(Obj o)
{
/* Write your sorting logic here.
this.val compared to o.val*/
return 0;
}
}
public void sortByValue(Map<String, ArrayList<>> mp){
ArrayList<Obj> arr=new ArrayList<Obj>();
for(String z:mp.keySet())//Make an object and store your map into the arrayList
{
Obj o=new Obj(z,mp.get(z));
arr.add(o);
}
System.out.println(arr);//Unsorted
Collections.sort(arr);// This sorts based on the conditions you coded in the compareTo function.
System.out.println(arr);//Sorted
}
A proper answer.
HashMap<Integer, Object> map = new HashMap<Integer, Object>();
ArrayList<Integer> sortedKeys = new ArrayList<Integer>(map.keySet());
Collections.sort(sortedKeys, new Comparator<Integer>() {
#Override
public int compare(Integer a, Integer b) {
return a.compareTo(b);
}
});
for (Integer key: sortedKeys) {
//map.get(key);
}
Note that HashMap itself cannot maintain sorting, as other answers have pointed out. It's a hash map, and hashed values are unsorted. You can thus either sort the keys when you need to and then access the values in order, as I demonstrated above, or you can find a different collection to store your data, like an ArrayList of Pairs/Tuples, such as the Pair found in Apache Commons:
https://commons.apache.org/proper/commons-lang/apidocs/org/apache/commons/lang3/tuple/Pair.html
Sorting by key:
public static void main(String[] args) {
Map<String,String> map = new HashMap<>();
map.put("b", "dd");
map.put("c", "cc");
map.put("a", "aa");
map = new TreeMap<>(map);
for (String key : map.keySet()) {
System.out.println(key+"="+map.get(key));
}
}
I developed a fully tested working solution. Hope it helps
import java.io.BufferedReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.StringTokenizer;
public class Main {
public static void main(String[] args) {
try {
BufferedReader in = new BufferedReader(new java.io.InputStreamReader (System.in));
String str;
HashMap<Integer, Business> hm = new HashMap<Integer, Business>();
Main m = new Main();
while ((str = in.readLine()) != null) {
StringTokenizer st = new StringTokenizer(str);
int id = Integer.parseInt(st.nextToken()); // first integer
int rating = Integer.parseInt(st.nextToken()); // second
Business a = m.new Business(id, rating);
hm.put(id, a);
List<Business> ranking = new ArrayList<Business>(hm.values());
Collections.sort(ranking, new Comparator<Business>() {
public int compare(Business i1, Business i2) {
return i2.getRating() - i1.getRating();
}
});
for (int k=0;k<ranking.size();k++) {
System.out.println((ranking.get(k).getId() + " " + (ranking.get(k)).getRating()));
}
}
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
public class Business{
Integer id;
Integer rating;
public Business(int id2, int rating2)
{
id=id2;
rating=rating2;
}
public Integer getId()
{
return id;
}
public Integer getRating()
{
return rating;
}
}
}
HashMap doesnt maintain any order, so if you want any kind of ordering, you need to store that in something else, which is a map and can have some kind of ordering, like LinkedHashMap
below is a simple program, by which you can sort by key, value, ascending ,descending ..( if you modify the compactor, you can use any kind of ordering, on keys and values)
package com.edge.collection.map;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
public class SortMapByKeyValue {
Map<String, Integer> map = new HashMap<String, Integer>();
public static void main(String[] args) {
SortMapByKeyValue smkv = new SortMapByKeyValue();
smkv.createMap();
System.out.println("After sorting by key ascending order......");
smkv.sortByKey(true);
System.out.println("After sorting by key descindeng order......");
smkv.sortByKey(false);
System.out.println("After sorting by value ascending order......");
smkv.sortByValue(true);
System.out.println("After sorting by value descindeng order......");
smkv.sortByValue(false);
}
void createMap() {
map.put("B", 55);
map.put("A", 80);
map.put("D", 20);
map.put("C", 70);
map.put("AC", 70);
map.put("BC", 70);
System.out.println("Before sorting......");
printMap(map);
}
void sortByValue(boolean order) {
List<Entry<String, Integer>> list = new LinkedList<Entry<String, Integer>>(map.entrySet());
Collections.sort(list, new Comparator<Entry<String, Integer>>() {
public int compare(Entry<String, Integer> o1, Entry<String, Integer> o2) {
if (order) {
return o1.getValue().compareTo(o2.getValue());
} else {
return o2.getValue().compareTo(o1.getValue());
}
}
});
Map<String, Integer> sortedMap = new LinkedHashMap<String, Integer>();
for (Entry<String, Integer> entry : list) {
sortedMap.put(entry.getKey(), entry.getValue());
}
printMap(sortedMap);
}
void sortByKey(boolean order) {
List<Entry<String, Integer>> list = new LinkedList<Entry<String, Integer>>(map.entrySet());
Collections.sort(list, new Comparator<Entry<String, Integer>>() {
public int compare(Entry<String, Integer> o1, Entry<String, Integer> o2) {
if (order) {
return o1.getKey().compareTo(o2.getKey());
} else {
return o2.getKey().compareTo(o1.getKey());
}
}
});
Map<String, Integer> sortedMap = new LinkedHashMap<String, Integer>();
for (Entry<String, Integer> entry : list) {
sortedMap.put(entry.getKey(), entry.getValue());
}
printMap(sortedMap);
}
public void printMap(Map<String, Integer> map) {
// System.out.println(map);
for (Entry<String, Integer> entry : map.entrySet()) {
System.out.println(entry.getKey() + " : " + entry.getValue());
}
}
}
here is the git link
Convert hashmap to a ArrayList with a pair class
Hashmap<Object,Object> items = new HashMap<>();
to
List<Pair<Object,Object>> items = new ArrayList<>();
so you can sort it as you want, or list sorted by adding order.
Sorting HashMap by value in Java:
public class HashMapSortByValue {
public static void main(String[] args) {
HashMap<Long,String> unsortMap = new HashMap<Long,String>();
unsortMap.put(5l,"B");
unsortMap.put(8l,"A");
unsortMap.put(2l, "D");
unsortMap.put(7l,"C" );
System.out.println("Before sorting......");
System.out.println(unsortMap);
HashMap<Long,String> sortedMapAsc = sortByComparator(unsortMap);
System.out.println("After sorting......");
System.out.println(sortedMapAsc);
}
public static HashMap<Long,String> sortByComparator(
HashMap<Long,String> unsortMap) {
List<Map.Entry<Long,String>> list = new LinkedList<Map.Entry<Long,String>>(
unsortMap.entrySet());
Collections.sort(list, new Comparator<Map.Entry<Long,String>> () {
public int compare(Map.Entry<Long,String> o1, Map.Entry<Long,String> o2) {
return o1.getValue().compareTo(o2.getValue());
}
});
HashMap<Long,String> sortedMap = new LinkedHashMap<Long,String>();
for (Entry<Long,String> entry : list) {
sortedMap.put(entry.getKey(), entry.getValue());
}
return sortedMap;
}
}

How to generalize a method to sort Map Entries in Java?

In my code, I've got a few maps. Then I have a method like the one below, which takes one of the maps, sorts the entries by their value and returns a list of the top ones, with the amount given by the parameter.
Example:
if the input map is like
"a" = 5,
"b" = 4,
"c" = 8,
"d" = 0,
and I call the method with quantity = 2 in the parameter, I'm given a list of the 2 highest map entries, sorted decreasingly
"c" = 8,
"a" = 5.
Right now, I have one such method for each of the maps and they only differ in:
the <Type1, Type2> declarations all over the method, and
the (distances.entrySet()); population of the all list.
Can I generalize this somehow to have just one alike method, being able to receive any of the types?
private static Map<String, Double> distances = new TreeMap<>();
private static Map<String, Integer> titles = new TreeMap<>();
private static Map<Integer, Integer> hours = new TreeMap<>();
private static Map<Date, Integer> days = new TreeMap<>();
public static List<Entry<String, Double>> getTopDistances(int quantity) {
List<Map.Entry<String, Double>> all = new ArrayList<>(distances.entrySet());
List<Map.Entry<String, Double>> requested = new ArrayList<>();
Collections.sort(all, new Comparator<Map.Entry<String, Double>>() {
#Override
public int compare(Entry<String, Double> e1, Entry<String, Double> e2) {
return (e2.getValue().compareTo(e1.getValue()));
}
});
int i = 0;
while (all.iterator().hasNext() && ++i <= quantity) {
requested.add(all.get(i - 1));
}
return requested;
}
I can surely continue with all the methods separated, but I sense a better way of doing it. Have researched generics, wildcards, collections and interfaces, which I think is the way to go, yet I still need a push.
With Java Streams one single line can produce what you want from the map to the final list. Bellow I package it in a private method which is a bit more neat but you could inline it if you want.
Since all your values are Comparable:
private <K, V extends Comparable<V>> List<Map.Entry<K,V>> getTop(Map<K,V> map, int quantity) {
return map.entrySet().stream()
.sorted((a,b) -> b.getValue().compareTo(a.getValue()))
.limit(quantity)
.collect(Collectors.toList());
}
If the value type was not comparable you then would need to pass the comparator as an additional parameter:
private <K, V> List<Map.Entry<K,V>> getTop(Map<K,V> map, Comparator<V> cmp, int quantity) {
return map.entrySet().stream()
.sorted((a,b) -> cmp.compare(b,a))
.limit(quantity)
.collect(Collectors.toList());
}
You cannot create comparator who will compare two comparables with different types, so you have to do this like this:
private static Map<String, Double> distances = new TreeMap<>();
private static Map<String, Integer> titles = new TreeMap<>();
private static Map<Integer, Integer> hours = new TreeMap<>();
private static Map<Date, Integer> days = new TreeMap<>();
public static List<Map.Entry<String, Double>> getTopDistances(int quantity) {
List<Map.Entry<String, Double>> all = new ArrayList<>(distances.entrySet());
List<Map.Entry<String, Double>> requested = new ArrayList<>();
all.sort(naturalOrder());
int i = 0;
while (all.iterator().hasNext() && ++i <= quantity) {
requested.add(all.get(i - 1));
}
return requested;
}
public static <T extends Comparable<? super T>> Comparator<Map.Entry<?,T> naturalOrder() {
return (e1, e2) -> e2.getValue().compareTo(e2.getValue());
}

Finding the Key from Value in Map

So I am having a bit of trouble understanding how to do this. I am building a word counter in Java using Map<String, Integer> where a word is the String and the amount of times that word was said in a .txt document is Integer. This is all working great...but I am trying to develop a part where it will display at the bottom what the top 5 results are ---> what the top 5 map.values() are.
The problem I have run into is after I find the value, I can't get the String with it.
Any suggestions would be very helpful to me.
You need to use the Map.Entry<String, Integer> to get the pair of the key and value.
The values() method returns only values, whereas the keySet() method returns only the keys.
Firstly, you should sort your map, based on values, to get the top five results.
The straightforward approach uses a Comparator. See the answer here for more.
Then you simply get the first five entries of the map.getEntrySet(). It would be easier to use an Iterator for this.
UPDATE:
Set<Entry<String, Integer>> set = wordCount.entrySet();
List<Entry<String, Integer>> list = new ArrayList<Entry<String, Integer>>(set);
Collections.sort(list, new Comparator<Map.Entry<String, Integer>>() {
public int compare(Map.Entry<String, Integer> o1, Map.Entry<String, Integer> o2) {
return o2.getValue().compareTo(o1.getValue());
}
});
int topResults = 5;
Iterator<Entry<String, Integer>> iter = list.iterator(); //refer the sorted collection
while (iter.hasNext() && topResults > 0 ) {
Map.Entry<String, Integer> entry = iter.next();
System.out.println(entry.getKey() + "->" + entry.getValue());
topResults --;
}
a) Iterate over the map, this way you have both keys and values accessible:
for (Map.Entry<String, Object> entry : map.entrySet()) {
String key = entry.getKey();
Integer value = entry.getValue();
// ...
}
b) build a second map for reverse lookup, e.g. Map - note that this is likely not a good idea here as you may have duplicate keys (same number for different words)
c) consider using a bidimap - this is a map which you can query both by key and by value.
I suggest you to override Comparator and build your Map constructor based on it.
Code is showed below:
class ValueComparator implements Comparator {
Map map;
public ValueComparator(Map map) {
this.map = map;
}
public int compare(Object keyA, Object keyB) {
Comparable valueA = (Comparable) map.get(keyA);
Comparable valueB = (Comparable) map.get(keyB);
return valueB.compareTo(valueA);
}
}
public class YourClass{
public static void main(String[] args) {
HashMap<String, Integer> map = new HashMap<String, Integer>();
map.put("a", 10);
map.put("b", 30);
map.put("c", 50);
map.put("d", 40);
map.put("e", 20);
System.out.println(map);
Map sortedMap = sortByValue(map);
System.out.println(sortedMap);
}
public static Map sortByValue(Map unsortedMap) {
Map sortedMap = new TreeMap(new ValueComparator(unsortedMap));
sortedMap.putAll(unsortedMap);
return sortedMap;
}
}

Java invert map

I need create inverse map - select unique values and for them find keys.
Seems that only way is to iterate all key/value pairs, because entrySet returns set of <key,value> so value not unique?
The values in a map may not be unique. But if they are (in your case) you can do as you wrote in your question and create a generic method to convert it:
private static <V, K> Map<V, K> invert(Map<K, V> map) {
Map<V, K> inv = new HashMap<V, K>();
for (Entry<K, V> entry : map.entrySet())
inv.put(entry.getValue(), entry.getKey());
return inv;
}
Java 8:
public static <V, K> Map<V, K> invert(Map<K, V> map) {
return map.entrySet()
.stream()
.collect(Collectors.toMap(Entry::getValue, Entry::getKey));
}
Example of usage:
public static void main(String[] args) {
Map<String, Integer> map = new HashMap<String, Integer>();
map.put("Hello", 0);
map.put("World!", 1);
Map<Integer, String> inv = invert(map);
System.out.println(inv); // outputs something like "{0=Hello, 1=World!}"
}
Side note: the put(.., ..) method will return the the "old" value for a key. If it is not null you may throw a new IllegalArgumentException("Map values must be unique") or something like that.
Take a look at Google Guava BiMap.
Example usage
Map<Integer, String> map = new HashMap<>();
map.put(1, "one");
map.put(2, "two");
Map<String, Integer> inverted = HashBiMap.create(map).inverse();
To get an inverted form of a given map in java 8:
public static <K, V> Map<V, K> inverseMap(Map<K, V> sourceMap) {
return sourceMap.entrySet().stream().collect(
Collectors.toMap(Entry::getValue, Entry::getKey,
(a, b) -> a) //if sourceMap has duplicate values, keep only first
);
}
Example usage
Map<Integer, String> map = new HashMap<Integer, String>();
map.put(1, "one");
map.put(2, "two");
Map<String, Integer> inverted = inverseMap(map);
Seems that only way is to iterate all key/value pairs, because entrySet returns set of so value not unique?
It's one way at least. Here's an example:
Map<Integer, String> map = new HashMap<Integer, String>();
map.put(1, "one");
map.put(2, "two");
Map<String, Integer> inverted = new HashMap<String, Integer>();
for (Integer i : map.keySet())
inverted.put(map.get(i), i);
In case of non-unique values, this algorithm will map the last value found to it's key. (Since the iteration order is undefined for most maps, this should be as good as any solution.)
If you really do want to keep the first value found for each key, you could change it to
if (!inverted.containsKey(map.get(i)))
inverted.put(map.get(i), i);
I would give another approach to this problem giving an extra dimension:
duplicate values in EntrySet.
public static void main(String[] args) {
HashMap<Integer, String> s = new HashMap<Integer, String>();
s.put(1, "Value1");
s.put(2, "Value2");
s.put(3, "Value2");
s.put(4, "Value1");
/*
* swap goes here
*/
HashMap<String,List<Integer>> newMap = new HashMap<String, List<Integer>>();
for (Map.Entry<Integer, String> en : s.entrySet()) {
System.out.println(en.getKey() + " " + en.getValue());
if(newMap.containsKey(en.getValue())){
newMap.get(en.getValue()).add(en.getKey());
} else {
List<Integer> tmpList = new ArrayList<Integer>();
tmpList.add(en.getKey());
newMap.put(en.getValue(), tmpList);
}
}
for(Map.Entry<String, List<Integer>> entry: newMap.entrySet()){
System.out.println(entry.getKey() + " " + entry.getValue());
}
}
T result will be that:
1 Value1 2 Value2 3 Value2 4 Value1 Value1 [1, 4] Value2 [2, 3]
Apache Commons Collections also provides a BidiMap interface for bi-directional maps, along with several implementations.
BidiMap JavaDoc
If your values duplicate and you need to store keys in list you can go with
val invertedMap = originalMap.entrySet().stream()
.collect(Collectors.groupingBy(
Map.Entry::getValue,
Collectors.mapping(Map.Entry::getKey, Collectors.toList()))
);
You have to assume that values may be identical, since the Map contract allows it.
In my opinion the best solution lies in using a wrapper. It will contain the original value, and add an id. Its hashCode() function will rely on the id, and you provide a Getter for the original value.
Code would be something like this:
public class MapKey
{
/**
* A new ID to differentiate equal values
*/
private int _id;
/**
* The original value now used as key
*/
private String _originalValue;
public MapKey(String originalValue)
{
_originalValue = originalValue;
//assuming some method for generating ids...
_id = getNextId();
}
public String getOriginalValue()
{
return _originalValue;
}
#Override
public int hashCode()
{
final int prime = 31;
int result = 1;
result = prime * result + _id;
return result;
}
#Override
public boolean equals(Object obj)
{
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
MapKey other = (MapKey) obj;
if (_id != other._id)
return false;
return true;
}
#Override
public String toString()
{
StringBuilder sb = new StringBuilder();
sb.append("MapKey value is ");
sb.append(_originalValue);
sb.append(" with ID number ");
sb.append(_id);
return sb.toString();
}
Inverting the map would be something like this:
public Map <MapKey, Integer> invertMap(Map <Integer, String> map)
{
Map <MapKey, Integer> invertedMap = new HashMap <MapKey, Integer>();
Iterator<Entry<Integer, String>> it = map.entrySet().iterator();
while(it.hasNext())
{
//getting the old values (to be reversed)
Entry<Integer, String> entry = it.next();
Integer oldKey = entry.getKey();
String oldValue = entry.getValue();
//creating the new MapKey
MapKey newMapKey = new MapKey(oldValue);
invertedMap.put(newMapKey, oldKey);
}
return invertedMap;
}
Printing the values something like this:
for(MapKey key : invertedMap.keySet())
{
System.out.println(key.toString() + " has a new value of " + invertedMap.get(key));
}
None of this code is tested, but I believe it's the best solution since it makes use of OO inheritance design instead of "c" style checks and allows you to display all the original keys and values.
With Guava
Multimaps.transformValues(Multimaps.index(map.entrySet(), Map.Entry::getValue),
Map.Entry::getKey)
You'll get a multimap (basically a map of lists) in return.

How to sort a HashMap in Java [duplicate]

This question already has answers here:
Sort a Map<Key, Value> by values
(64 answers)
Closed 3 years ago.
How are we able to sort a HashMap<key, ArrayList>?
I want to sort on the basis of a value in the ArrayList.
Do you have to use a HashMap? If you only need the Map Interface use a TreeMap
If you want to sort by comparing values in the HashMap. You have to write code to do this, if you want to do it once you can sort the values of your HashMap:
Map<String, Person> people = new HashMap<>();
Person jim = new Person("Jim", 25);
Person scott = new Person("Scott", 28);
Person anna = new Person("Anna", 23);
people.put(jim.getName(), jim);
people.put(scott.getName(), scott);
people.put(anna.getName(), anna);
// not yet sorted
List<Person> peopleByAge = new ArrayList<>(people.values());
Collections.sort(peopleByAge, Comparator.comparing(Person::getAge));
for (Person p : peopleByAge) {
System.out.println(p.getName() + "\t" + p.getAge());
}
If you want to access this sorted list often, then you could insert your elements into a HashMap<TreeSet<Person>>, though the semantics of sets and lists are a bit different.
Sorted List by hasmap keys:
SortedSet<String> keys = new TreeSet<String>(myHashMap.keySet());
Sorted List by hashmap values:
SortedSet<String> values = new TreeSet<String>(myHashMap.values());
In case of duplicated map values:
List<String> mapValues = new ArrayList<String>(myHashMap.values());
Collections.sort(mapValues);
Good Luck!
http://snipplr.com/view/2789/sorting-map-keys-by-comparing-its-values/
get the keys
List keys = new ArrayList(yourMap.keySet());
Sort them
Collections.sort(keys)
print them.
In any case, you can't have sorted values in HashMap (according to API This class makes no guarantees as to the order of the map; in particular, it does not guarantee that the order will remain constant over time ].
Though you can push all these values to LinkedHashMap, for later use as well.
Seems like you might want a treemap.
http://docs.oracle.com/javase/7/docs/api/java/util/TreeMap.html
You can pass in a custom comparator to it if that applies.
In Java 8:
Comparator<Entry<String, Item>> valueComparator =
(e1, e2) -> e1.getValue().getField().compareTo(e2.getValue().getField());
Map<String, Item> sortedMap =
unsortedMap.entrySet().stream().
sorted(valueComparator).
collect(Collectors.toMap(Entry::getKey, Entry::getValue,
(e1, e2) -> e1, LinkedHashMap::new));
Using Guava:
Map<String, Item> map = ...;
Function<Item, Integer> getField = new Function<Item, Integer>() {
public Integer apply(Item item) {
return item.getField(); // the field to sort on
}
};
comparatorFunction = Functions.compose(getField, Functions.forMap(map));
comparator = Ordering.natural().onResultOf(comparatorFunction);
Map<String, Item> sortedMap = ImmutableSortedMap.copyOf(map, comparator);
Custom compare function which includes functionality for the Turkish alphabet or other different languages than english.
public <K extends Comparable,V extends Comparable> LinkedHashMap<K,V> sortByKeys(LinkedHashMap<K,V> map){
List<K> keys = new LinkedList<K>(map.keySet());
Collections.sort(keys, (Comparator<? super K>) new Comparator<String>() {
#Override
public int compare(String first, String second) {
Collator collator = Collator.getInstance(Locale.getDefault());
//Collator collator = Collator.getInstance(new Locale("tr", "TR"));
return collator.compare(first, second);
}
});
LinkedHashMap<K,V> sortedMap = new LinkedHashMap<K,V>();
for(K key: keys){
sortedMap.put(key, map.get(key));
}
return sortedMap;
}
here is the using example as the following
LinkedHashMap<String, Boolean> ligList = new LinkedHashMap<String, Boolean>();
ligList = sortByKeys(ligList);
Without any more information, it's hard to know exactly what you want. However, when choosing what data structure to use, you need to take into account what you need it for. Hashmaps are not designed for sorting - they are designed for easy retrieval. So in your case, you'd probably have to extract each element from the hashmap, and put them into a data structure more conducive to sorting, such as a heap or a set, and then sort them there.
If you want to combine a Map for efficient retrieval with a SortedMap, you may use the ConcurrentSkipListMap.
Of course, you need the key to be the value used for sorting.
have you considered using a LinkedHashMap<>()..?
public static void main(String[] args) {
Map<Object, Object> handler = new LinkedHashMap<Object, Object>();
handler.put("item", "Value");
handler.put(2, "Movies");
handler.put("isAlive", true);
for (Map.Entry<Object, Object> entrY : handler.entrySet())
System.out.println(entrY.getKey() + ">>" + entrY.getValue());
List<Map.Entry<String, Integer>> entries = new ArrayList<Map.Entry<String, Integer>>();
Collections.sort(entries, new Comparator<Map.Entry<String, Integer>>() {
public int compare(Map.Entry<String, Integer> a,
Map.Entry<String, Integer> b) {
return a.getValue().compareTo(b.getValue());
}
});
}
results into an organized linked object.
item>>Value
2>>Movies
isAlive>>true
check the sorting part picked from here..
I have developed a class which can be used to sort a map on the basis of keys and values. The basic idea is if you have sort a map using keys then create a TreepMap from your Map which will sort the map by keys. And in case of sorting by values create a list from entrySet and sort the list using comparator interface.
Here is the full solution :
public static void main(String[] args) {
Map<String, Integer> unSortedMap = new LinkedHashMap<String, Integer>();
unSortedMap.put("A", 2);
unSortedMap.put("V", 1);
unSortedMap.put("G", 5);
System.out.println("Unsorted Map :\n");
for (Map.Entry<String, Integer> entry : unSortedMap.entrySet()) {
System.out.println(entry.getKey() + " " + entry.getValue());
}
System.out.println("\n");
System.out.println("Sorting Map Based on Keys :\n");
Map<String, Integer> keySortedMap = new TreeMap<String, Integer>(unSortedMap);
for (Map.Entry<String, Integer> entry : keySortedMap.entrySet()) {
System.out.println(entry.getKey() + " " + entry.getValue());
}
System.out.println("\n");
System.out.println("Sorting Map Based on Values :\n");
List<Entry<String, Integer>> entryList = new ArrayList<Entry<String, Integer>>(unSortedMap.entrySet());
Collections.sort(entryList, new Comparator<Entry<String, Integer>>() {
#Override
public int compare(Entry<String, Integer> obj1, Entry<String, Integer> obj2) {
return obj1.getValue().compareTo(obj2.getValue());
}
});
unSortedMap.clear();
for (Entry<String, Integer> entry : entryList) {
unSortedMap.put(entry.getKey(), entry.getValue());
System.out.println(entry.getKey() + " " + entry.getValue());
}
}
Code is properly tested :D
Sorting HashMap by Value:
As others have pointed out. HashMaps are for easy lookups if you change that or try to sort inside the map itself you will no longer have O(1) lookup.
The code for your sorting is as follows:
class Obj implements Comparable<Obj>{
String key;
ArrayList<Integer> val;
Obj(String key, ArrayList<Integer> val)
{
this.key=key;
this.val=val;
}
public int compareTo(Obj o)
{
/* Write your sorting logic here.
this.val compared to o.val*/
return 0;
}
}
public void sortByValue(Map<String, ArrayList<>> mp){
ArrayList<Obj> arr=new ArrayList<Obj>();
for(String z:mp.keySet())//Make an object and store your map into the arrayList
{
Obj o=new Obj(z,mp.get(z));
arr.add(o);
}
System.out.println(arr);//Unsorted
Collections.sort(arr);// This sorts based on the conditions you coded in the compareTo function.
System.out.println(arr);//Sorted
}
A proper answer.
HashMap<Integer, Object> map = new HashMap<Integer, Object>();
ArrayList<Integer> sortedKeys = new ArrayList<Integer>(map.keySet());
Collections.sort(sortedKeys, new Comparator<Integer>() {
#Override
public int compare(Integer a, Integer b) {
return a.compareTo(b);
}
});
for (Integer key: sortedKeys) {
//map.get(key);
}
Note that HashMap itself cannot maintain sorting, as other answers have pointed out. It's a hash map, and hashed values are unsorted. You can thus either sort the keys when you need to and then access the values in order, as I demonstrated above, or you can find a different collection to store your data, like an ArrayList of Pairs/Tuples, such as the Pair found in Apache Commons:
https://commons.apache.org/proper/commons-lang/apidocs/org/apache/commons/lang3/tuple/Pair.html
Sorting by key:
public static void main(String[] args) {
Map<String,String> map = new HashMap<>();
map.put("b", "dd");
map.put("c", "cc");
map.put("a", "aa");
map = new TreeMap<>(map);
for (String key : map.keySet()) {
System.out.println(key+"="+map.get(key));
}
}
I developed a fully tested working solution. Hope it helps
import java.io.BufferedReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.StringTokenizer;
public class Main {
public static void main(String[] args) {
try {
BufferedReader in = new BufferedReader(new java.io.InputStreamReader (System.in));
String str;
HashMap<Integer, Business> hm = new HashMap<Integer, Business>();
Main m = new Main();
while ((str = in.readLine()) != null) {
StringTokenizer st = new StringTokenizer(str);
int id = Integer.parseInt(st.nextToken()); // first integer
int rating = Integer.parseInt(st.nextToken()); // second
Business a = m.new Business(id, rating);
hm.put(id, a);
List<Business> ranking = new ArrayList<Business>(hm.values());
Collections.sort(ranking, new Comparator<Business>() {
public int compare(Business i1, Business i2) {
return i2.getRating() - i1.getRating();
}
});
for (int k=0;k<ranking.size();k++) {
System.out.println((ranking.get(k).getId() + " " + (ranking.get(k)).getRating()));
}
}
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
public class Business{
Integer id;
Integer rating;
public Business(int id2, int rating2)
{
id=id2;
rating=rating2;
}
public Integer getId()
{
return id;
}
public Integer getRating()
{
return rating;
}
}
}
HashMap doesnt maintain any order, so if you want any kind of ordering, you need to store that in something else, which is a map and can have some kind of ordering, like LinkedHashMap
below is a simple program, by which you can sort by key, value, ascending ,descending ..( if you modify the compactor, you can use any kind of ordering, on keys and values)
package com.edge.collection.map;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
public class SortMapByKeyValue {
Map<String, Integer> map = new HashMap<String, Integer>();
public static void main(String[] args) {
SortMapByKeyValue smkv = new SortMapByKeyValue();
smkv.createMap();
System.out.println("After sorting by key ascending order......");
smkv.sortByKey(true);
System.out.println("After sorting by key descindeng order......");
smkv.sortByKey(false);
System.out.println("After sorting by value ascending order......");
smkv.sortByValue(true);
System.out.println("After sorting by value descindeng order......");
smkv.sortByValue(false);
}
void createMap() {
map.put("B", 55);
map.put("A", 80);
map.put("D", 20);
map.put("C", 70);
map.put("AC", 70);
map.put("BC", 70);
System.out.println("Before sorting......");
printMap(map);
}
void sortByValue(boolean order) {
List<Entry<String, Integer>> list = new LinkedList<Entry<String, Integer>>(map.entrySet());
Collections.sort(list, new Comparator<Entry<String, Integer>>() {
public int compare(Entry<String, Integer> o1, Entry<String, Integer> o2) {
if (order) {
return o1.getValue().compareTo(o2.getValue());
} else {
return o2.getValue().compareTo(o1.getValue());
}
}
});
Map<String, Integer> sortedMap = new LinkedHashMap<String, Integer>();
for (Entry<String, Integer> entry : list) {
sortedMap.put(entry.getKey(), entry.getValue());
}
printMap(sortedMap);
}
void sortByKey(boolean order) {
List<Entry<String, Integer>> list = new LinkedList<Entry<String, Integer>>(map.entrySet());
Collections.sort(list, new Comparator<Entry<String, Integer>>() {
public int compare(Entry<String, Integer> o1, Entry<String, Integer> o2) {
if (order) {
return o1.getKey().compareTo(o2.getKey());
} else {
return o2.getKey().compareTo(o1.getKey());
}
}
});
Map<String, Integer> sortedMap = new LinkedHashMap<String, Integer>();
for (Entry<String, Integer> entry : list) {
sortedMap.put(entry.getKey(), entry.getValue());
}
printMap(sortedMap);
}
public void printMap(Map<String, Integer> map) {
// System.out.println(map);
for (Entry<String, Integer> entry : map.entrySet()) {
System.out.println(entry.getKey() + " : " + entry.getValue());
}
}
}
here is the git link
Convert hashmap to a ArrayList with a pair class
Hashmap<Object,Object> items = new HashMap<>();
to
List<Pair<Object,Object>> items = new ArrayList<>();
so you can sort it as you want, or list sorted by adding order.
Sorting HashMap by value in Java:
public class HashMapSortByValue {
public static void main(String[] args) {
HashMap<Long,String> unsortMap = new HashMap<Long,String>();
unsortMap.put(5l,"B");
unsortMap.put(8l,"A");
unsortMap.put(2l, "D");
unsortMap.put(7l,"C" );
System.out.println("Before sorting......");
System.out.println(unsortMap);
HashMap<Long,String> sortedMapAsc = sortByComparator(unsortMap);
System.out.println("After sorting......");
System.out.println(sortedMapAsc);
}
public static HashMap<Long,String> sortByComparator(
HashMap<Long,String> unsortMap) {
List<Map.Entry<Long,String>> list = new LinkedList<Map.Entry<Long,String>>(
unsortMap.entrySet());
Collections.sort(list, new Comparator<Map.Entry<Long,String>> () {
public int compare(Map.Entry<Long,String> o1, Map.Entry<Long,String> o2) {
return o1.getValue().compareTo(o2.getValue());
}
});
HashMap<Long,String> sortedMap = new LinkedHashMap<Long,String>();
for (Entry<Long,String> entry : list) {
sortedMap.put(entry.getKey(), entry.getValue());
}
return sortedMap;
}
}

Categories