I have a HashMap with the following structure,
{val1#val2=val3#val4-val5}
where key = val1#val2 and value=val3#val4-val5 ,
HashMap<String, String> h = new HashMap<String, String>();
h.put("aaa#bbb", "111#444-555");
h.put("bbb#aaa", "222#ddd-222");
h.put("111#999", "000#213-aaa");
I have three conditions where I have to sort the map as,
1. By val1.
2. By val2.
3. By val3.
HashMaps don't guarantee order, in order to get sorted map you need to use LinkedHashMap.
To sort the keys you could use the java stream api, sort the map entries and the insert them in LinkedHashMap.
Map<String, String> h = new HashMap<>();
h.put("aaa#bbb", "111#444-555");
h.put("bbb#aaa", "222#ddd-222");
h.put("111#999", "000#213-aaa");
LinkedHashMap<String, String> linkedHashMap = new LinkedHashMap<>();
h.entrySet().stream()
.sorted(Comparator.comparing(e -> e.getKey().split("#")[0]))// sort by val1
.sorted(Comparator.comparing(e -> e.getKey().split("#")[1]))// sort by val2
.sorted(Comparator.comparing(e -> e.getValue().split("#")[0]))// sort by val3
.forEach(e -> {
linkedHashMap.put(e.getKey(), e.getValue());
});
This should work:
public static void main(String[] args) {
LinkedHashMap<String, String> map = new LinkedHashMap<>();
map.put("aa#bb", "11111#44-5555555");
map.put("bb#aa", "22222#ddd-222");
map.put("11#99", "00000#213-aaa");
Function<Map.Entry<String, String>, String> byVal1 =
entry -> entry.getKey().substring(0, entry.getKey().indexOf('#'));
Function<Map.Entry<String, String>, String> byVal2 =
entry -> entry.getKey().substring(entry.getKey().indexOf('#') + 1);
Function<Map.Entry<String, String>, String> byVal3 =
entry -> entry.getValue().substring(0, entry.getValue().indexOf('#'));
// Just change this value to sort by a different value
Function<Map.Entry<String, String>, String> value = byVal3;
List<Map.Entry<String, String>> asList = map.entrySet().stream().sorted(Comparator.comparing(value)).collect(Collectors.toList());
map.clear();
asList.forEach(entry -> map.put(entry.getKey(), entry.getValue()));
map.entrySet().stream().forEach(entry -> System.out.println(entry));
}
You of course don't have to create the byValX functions in the method and could use method references instead.
No idea if that is what you want to accomplish (clarify what needs to be done and I can adapt the solution), but you can use a TreeMap:
import java.util.Map;
import java.util.Objects;
import java.util.TreeMap;
public class TestDictionary {
public static void main(String[] args) {
Map<Key, Object> map = new TreeMap<>(new Comparator<Key>() {
#Override
public int compare(Key o1, Key o2) {
// do whatever you want here
return 0;
}
});
map.put(new Key("a", "b", "c"), new Value());
map.put(new Key("b", "c", "a"), new Value());
map.put(new Key("c", "b", "a"), new Value());
System.out.println(map);
}
static class Key {
String val1;
String val2;
String val3;
public Key(String val1, String val2, String val3) {
this.val1 = val1;
this.val2 = val2;
this.val3 = val3;
}
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Key key = (Key) o;
return val1.equals(key.val1) &&
val2.equals(key.val2) &&
val3.equals(key.val3);
}
#Override
public int hashCode() {
return Objects.hash(val1, val2, val3);
}
}
static class Value {
int number;
}
}
Related
I have an Map Map1 and another Map Map2..
Map<Key,ProductEntity> Map1;
Map<Key, ProductEntitySecond> Map2;
I want to iterate in map1 and check if that particular key of Map1 is present in Map2. if it is then return the value .
Can someone please give me an idea to solve that type of problem.
Maybe you can do something like this:
map1
.entrySet()
.stream()
.filter(e -> map2.containsKey(e.getKey()))
.findFirst()
.map(e -> e.getValue())
.orElse(null);
So if the key is present in map2 it'll return the value from map1 otherwise, it'll return null
You can use the Stream API.
public class Application {
public static void main(String[] args) {
var map1 = new HashMap<Square, String>();
map1.put(new Square(2), "two");
map1.put(new Square(3), "three");
map1.put(new Square(4), "four");
map1.put(new Square(5), "five");
var map2 = new HashMap<Square, String>();
map2.put(new Square(2), "two");
map2.put(new Square(3), "three");
var valuesAlsoInMap2 = map1.entrySet().stream()
.filter(it -> map2.containsKey(it.getKey()))
.map(Map.Entry::getValue)
.toList();
System.out.println(valuesAlsoInMap2);
}
}
Expected result:
[two, three]
This is using a simple POJO Square to show you it does work with non-primitive types. To see which methods HashMap relies on have a look at this thread.
import java.util.Objects;
class Square extends shape{
private int side;
public Square(int side) {
this.side = side;
}
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Square square = (Square) o;
return side == square.side;
}
#Override
public int hashCode() {
return Objects.hash(side);
}
#Override
public double Area() {
return side*side;
}
public int getSide() {
return side;
}
public void setSide(int side) {
this.side = side;
}
}
Here's another approach using an intersection operation (retainAll()) on the two maps' key sets.
Map<Key, ProductEntity> map1 = ...;
Map<Key, ProductEntitySecond> map2 = ...;
Map<Key, ProductionEntity> tmp = new HashMap<>(map1);
tmp.keySet().retainAll(map2.keySet());
Collection<ProductionEntity> shared = tmp.values();
This is simple and clean, but it's not the most efficient solution, if that matters in this case.
I will leave you with a very, very simplified example.
If you choose to go with a solution similar to this one I would strongly recommend you to read about how to overwrite Equals and Hashcode: https://www.baeldung.com/java-equals-hashcode-contracts
import java.util.Map;
import java.util.HashMap;
public class HelloWorld{
public static void main(String []args) {
Entity entity1 = new Entity();
entity1.id = 1L;
Entity entity2 = new Entity();
entity2.id = 1L;
Map map1 = new HashMap<Entity, String>();
map1.put(entity1, "one");
Map map2 = new HashMap<Entity, String>();
map2.put(entity2, "two");
System.out.println(map1.get(entity1));
System.out.println(map2.get(entity1));
}
private static class Entity {
public Long id;
#Override
public boolean equals(Object o) {
return ((Entity) o).id.equals(this.id);
}
}
}
As you can see even two different references but with same id are now considered equals.
for Any java version 8+
List<String> valuesFromMap2WhenKeysAreCommon =
map1.keySet().stream().filter(map1::containsKey).map(map2::get).collect(Collectors.toList());
I want to store key-value pairs in TreeMap and sort the entries based on the value of Key as per following logic:
Sort by the length of the key. If the length of two keys is same then sort them alphabetically. Example, for the following key-value pairs.
IBARAKI MitoCity
TOCHIGI UtunomiyaCity
GUNMA MaehashiCity
SAITAMA SaitamaCity
CHIBA ChibaCity
TOKYO Sinjyuku
KANAGAWA YokohamaCity
The expected output is like this.
CHIBA : ChibaCity
GUNMA : MaehashiCity
TOKYO : Sinjyuku
IBARAKI : MitoCity
SAITAMA : SaitamaCity
TOCHIGI : UtunomiyaCity
KANAGAWA : YokohamaCity
You can pass the Comparator as a parameter to Map's constructor.
According to documentation it is used for Keys only:
/**
* Constructs a new, empty tree map, ordered according to the given
* comparator. All keys inserted into the map must be <em>mutually
* comparable</em> by the given comparator: {#code comparator.compare(k1,
* k2)} must not throw a {#code ClassCastException} for any keys
* {#code k1} and {#code k2} in the map. If the user attempts to put
* a key into the map that violates this constraint, the {#code put(Object
* key, Object value)} call will throw a
* {#code ClassCastException}.
*
* #param comparator the comparator that will be used to order this map.
* If {#code null}, the {#linkplain Comparable natural
* ordering} of the keys will be used.
*/
public TreeMap(Comparator<? super K> comparator) {
this.comparator = comparator;
}
In this way you can pass comparator by length of your key like this:
new TreeMap<>(Comparator.comparingInt(String::length).thenComparing(Comparator.naturalOrder()))
You need to write your own comparator for this and use it in TreeMap, e.g.:
public class StringComparator implements Comparator<String> {
#Override
public int compare(String s1, String s2) {
return s1.length() == s2.length() ? s1.compareTo(s2) : s1.length() - s2.length();
}
public static void main(String[] args) throws JsonParseException, JsonMappingException, IOException {
Map<String, String> map = new TreeMap<>(new StringComparator());
map.put("IBARAKI", "MitoCity");
map.put("TOCHIGI", "UtunomiyaCity");
map.put("GUNMA", "MaehashiCity");
map.put("SAITAMA", "SaitamaCity");
map.put("CHIBA", "ChibaCity");
map.put("TOKYO", "Sinjyuku");
map.put("KANAGAWA", "YokohamaCity");
System.out.println(map);
}
}
This does not handle null values but you can add the handling if you are expecting null values in your use case.
You should create a unique comparator for comparing the keys of the map. But because you want to print the values too, you should compare the whole entrysets instead:
Comparator<Map.Entry<String, String>> c = new Comparator<Map.Entry<String, String>>() {
#Override
public int compare(Map.Entry<String, String> o1, Map.Entry<String, String> o2) {
int q = Integer.compare(o1.getKey().length(), o2.getKey().length());
return q != 0 ? q : o1.getKey().compareTo(o2.getKey());
}
};
Then you can use this comparator in sorting:
map.entrySet().stream().sorted(c).forEach(System.out::println);
You can do this as follows.
public static void main(String[] args) {
Map<String, String> map = new TreeMap<>(new CustomSortComparator());
map.put("IBARAKI", "MitoCity");
map.put("TOCHIGI", "UtunomiyaCity");
map.put("GUNMA", "MaehashiCity");
map.put("SAITAMA", "SaitamaCity");
map.put("CHIBA", "ChibaCity");
map.put("TOKYO", "Sinjyuku");
map.put("KANAGAWA", "YokohamaCity");
System.out.println(map);
}
The CustomSortComparator has been defined as follows.
public class CustomSortComparator implements Comparator<String> {
#Override
public int compare(String o1, String o2) {
if (o1.length() > o2.length()) {
return 1;
}
if (o1.length() < o2.length()) {
return -1;
}
return returnCompareBytes(o1, o2);
}
private int returnCompareBytes(String key1, String key2) {
for (int i = 0; i < key1.length() - 1; i++) {
if (key1.charAt(i) > key2.charAt(i)) {
return 1;
}
if (key1.charAt(i) < key2.charAt(i)) {
return -1;
}
}
return 0;
}
}
Instead of converting Map into TreeMap directly you can use this method
public static Map toTreeMap(Map hashMap)
{
// Create a new TreeMap
Map treeMap = new TreeMap<>(new Comparator<Map.Entry<String, String>>(){
public int compare(Map.Entry<String, String> o1, Map.Entry<String, String> o2 )
{
if (o1.getKey().length() > o2.getKey().length()) {
return 1;
}
if (o1.getKey().length() > o2.getKey().length()) {
return -1;
}
return o1.getKey().compareTo(o2.getKey());
}
});
for(Map.entry e : hashmap){
treeMap.put(e.getKey(),e.getValue);
}
return treeMap;
}
You can define the Comparator<String> you need in the constructor call to the TreeMap:
import java.util.Comparator;
import java.util.Map;
import java.util.TreeMap;
public class Main {
static final Map<String, String> map =
new TreeMap<String, String> (new Comparator<String>() {
#Override
public int compare(String o1, String o2) {
int diff_length = o1.length() - o2.length();
if (diff_length != 0) return diff_length;
return o1.compareTo(o2);
}
});
public static final void main(String[] args) {
map.put("IBARAKI", "MitoCity");
map.put("TOCHIGI", "UtunomiyaCity");
map.put("GUNMA", "MaehashiCity");
map.put("SAITAMA", "SaitamaCity");
map.put("CHIBA", "ChibaCity");
map.put("TOKYO", "Sinjyuku");
map.put("KANAGAWA", "YokohamaCity");
System.out.println(map);
}
}
I am trying to resolve sorting a map , which contains huge data(1000K).
Is there any efficient way than this to sorting these maps ?
below is the code snippet.
Map<Integer, String> myMap1 = new HashMap<Integer, String>();
Map<String,Integer> myMap2 = new HashMap< String,Integer>();
List <Entry<Integer,String>> lst1 = new ArrayList<Entry<Integer,String>>(myMap1.entrySet());
Collections.sort(lst1, new Comparator<Entry<Integer,String>>(){
#Override
public int compare(Entry e1, Entry e2)
{
return ((String) e1.getValue()).compareTo((String) e2.getValue());
}}
);
List <Entry<String,Integer>> lst2 = new ArrayList<Entry<String,Integer>>(myMap2.entrySet());
Collections.sort(lst2, new Comparator<Entry<String,Integer>>(){
#Override
public int compare(Entry e1, Entry e2)
{
return ((Integer) e1.getValue()).compareTo((Integer) e2.getValue());
}}
);
IMO a priority queue can also be a good approach:
Map<Integer, String> myMap1 = new HashMap<Integer, String>();
PriorityQueue<Entry<Integer, String>> pq = new PriorityQueue<Map.Entry<Integer,String>>(myMap1.size(), new Comparator<Entry<Integer, String>>() {
#Override
public int compare(Entry<Integer, String> arg0, Entry<Integer, String> arg1) {
return arg0.getValue().compareTo(arg1.getValue());
}
});
pq.addAll(myMap1.entrySet());
while (!pq.isEmpty()) {
System.out.println(pq.poll());
}
Also Google Guava can be a good option as it provides a BiMap implementations which can be inversed, and then just sort on inversed map keys.
Map<Integer, String> myMap1 = new HashMap<Integer, String>();
// insert values in myMap
Map<String,Integer> myMap2 = myMap1.inverse();
SortedMap<Integer, Character> sortedInversed = new TreeMap<Integer, Character>(myMap2 );
I have a HashMap and I would like to get a new HashMap that contains only the elements from the first HashMap where K belongs to a specific List.
I could look through all the keys and fillup a new HashMap but I was wondering if there is a more efficient way to do it?
thanks
With Java8 streams, there is a functional (elegant) solution. If keys is the list of keys to keep and map is the source Map.
keys.stream()
.filter(map::containsKey)
.collect(Collectors.toMap(Function.identity(), map::get));
Complete example:
List<Integer> keys = new ArrayList<>();
keys.add(2);
keys.add(3);
keys.add(42); // this key is not in the map
Map<Integer, String> map = new HashMap<>();
map.put(1, "foo");
map.put(2, "bar");
map.put(3, "fizz");
map.put(4, "buz");
Map<Integer, String> res = keys.stream()
.filter(map::containsKey)
.collect(Collectors.toMap(Function.identity(), map::get));
System.out.println(res.toString());
Prints: {2=bar, 3=fizz}
EDIT add a filter for keys that are absent from the map
Yes there is a solution:
Map<K,V> myMap = ...;
List<K> keysToRetain = ...;
myMap.keySet().retainAll(keysToRetain);
The retainAll operation on the Set updates the underlying map. See java doc.
Edit
Be aware this solution modify the Map.
With a help of Guava.
Suppose you have a map Map<String, String> and want to submap with a values from List<String> list.
Map<String, String> map = new HashMap<>();
map.put("1", "1");
map.put("2", "2");
map.put("3", "4");
final List<String> list = Arrays.asList("2", "4");
Map<String, String> subMap = Maps.filterValues(
map, Predicates.in(list));
Update / Note: As #assylias mentioned in the comment, you will have O(n) when using contains(). So if you have large list, this could have huge impact in performance.
On the other side HashSet.contains() is constant time O(1), so if there is a possibility to have Set instead of List, this could be a nice approach (note that converting List to Set will cost O(n) anyway, so better not to convert :))
If you have Map m1 and List keys, then try following
Map m2 = new HashMap(m1);
m2.keySet().retainAll(keys);
Depending on your usage, this may be a more efficient implementation
public class MapView implements Map{
List ak;
Map map;
public MapView(Map map, List allowableKeys) {
ak = allowableKeys;
map = map;
}
public Object get(Object key) {
if (!ak.contains(key)) return null;
return map.get(key);
}
}
If your keys have an ordering, you can use a TreeMap.
Look at TreeMap.subMap()
It does not let you do this using a list, though.
You could even grow your own:
public class FilteredMap<K, V> extends AbstractMap<K, V> implements Map<K, V> {
// The map I wrap.
private final Map<K, V> map;
// The filter.
private final Set<K> filter;
public FilteredMap(Map<K, V> map, Set<K> filter) {
this.map = map;
this.filter = filter;
}
#Override
public Set<Entry<K, V>> entrySet() {
// Make a new one to break the bond with the underlying map.
Set<Entry<K, V>> entries = new HashSet<>(map.entrySet());
Set<Entry<K, V>> remove = new HashSet<>();
for (Entry<K, V> entry : entries) {
if (!filter.contains(entry.getKey())) {
remove.add(entry);
}
}
entries.removeAll(remove);
return entries;
}
}
public void test() {
Map<String, String> map = new HashMap<>();
map.put("1", "One");
map.put("2", "Two");
map.put("3", "Three");
Set<String> filter = new HashSet<>();
filter.add("1");
filter.add("2");
Map<String, String> filtered = new FilteredMap<>(map, filter);
System.out.println(filtered);
}
If you're concerned about all of the copying you could also grow a filtered Set and a filterd Iterator instead.
public interface Filter<T> {
public boolean accept(T t);
}
public class FilteredIterator<T> implements Iterator<T> {
// The Iterator
private final Iterator<T> i;
// The filter.
private final Filter<T> filter;
// The next.
private T next = null;
public FilteredIterator(Iterator<T> i, Filter<T> filter) {
this.i = i;
this.filter = filter;
}
#Override
public boolean hasNext() {
while (next == null && i.hasNext()) {
T n = i.next();
if (filter.accept(n)) {
next = n;
}
}
return next != null;
}
#Override
public T next() {
T n = next;
next = null;
return n;
}
}
public class FilteredSet<K> extends AbstractSet<K> implements Set<K> {
// The Set
private final Set<K> set;
// The filter.
private final Filter<K> filter;
public FilteredSet(Set<K> set, Filter<K> filter) {
this.set = set;
this.filter = filter;
}
#Override
public Iterator<K> iterator() {
return new FilteredIterator(set.iterator(), filter);
}
#Override
public int size() {
int n = 0;
Iterator<K> i = iterator();
while (i.hasNext()) {
i.next();
n += 1;
}
return n;
}
}
public class FilteredMap<K, V> extends AbstractMap<K, V> implements Map<K, V> {
// The map I wrap.
private final Map<K, V> map;
// The filter.
private final Filter<K> filter;
public FilteredMap(Map<K, V> map, Filter<K> filter) {
this.map = map;
this.filter = filter;
}
#Override
public Set<Entry<K, V>> entrySet() {
return new FilteredSet<>(map.entrySet(), new Filter<Entry<K, V>>() {
#Override
public boolean accept(Entry<K, V> t) {
return filter.accept(t.getKey());
}
});
}
}
public void test() {
Map<String, String> map = new HashMap<>();
map.put("1", "One");
map.put("2", "Two");
map.put("3", "Three");
Set<String> filter = new HashSet<>();
filter.add("1");
filter.add("2");
Map<String, String> filtered = new FilteredMap<>(map, new Filter<String>() {
#Override
public boolean accept(String t) {
return filter.contains(t);
}
});
System.out.println(filtered);
}
Instead of looking through all keys you could loop over the list and check if the HashMap contains a mapping. Then create a new HashMap with the filtered entries:
List<String> keys = Arrays.asList('a', 'c', 'e');
Map<String, String> old = new HashMap<>();
old.put('a', 'aa');
old.put('b', 'bb');
old.put('c', 'cc');
old.put('d', 'dd');
old.put('e', 'ee');
// only use an inital capacity of keys.size() if you won't add
// additional entries to the map; anyways it's more of a micro optimization
Map<String, String> newMap = new HashMap<>(keys.size(), 1f);
for (String key: keys) {
String value = old.get(key);
if (value != null) newMap.put(key, value);
}
Copy the map and remove all keys not in the list:
Map map2 = new Hashmap(map);
map2.keySet().retainAll(keysToKeep);
you can use the clone() method on the K HashMap returned.
something like this:
import java.util.HashMap;
public class MyClone {
public static void main(String a[]) {
Map<String, HashMap<String, String>> hashMap = new HashMap<String, HashMap<String, String>>();
Map hashMapCloned = new HashMap<String, String>();
Map<String, String> insert = new HashMap<String, String>();
insert.put("foo", "bar");
hashMap.put("first", insert);
hashMapCloned.put((HashMap<String, String>) hashMap.get("first").clone());
}
}
It may have some syntax errors because I haven't tested, but try something like that.
No, because HashMap doesn't maintain an order of it's entries. You can use TreeMap if you need a sub map between some range. And also, please look at this question; it seems to be on the similar lines of yours.
You asked for a new HashMap. Since HashMap does not support structure sharing, there is no better approach than the obvious one. (I have assumed here that null cannot be a value).
Map<K, V> newMap = new HashMap<>();
for (K k : keys) {
V v = map.get(k);
if (v != null)
newMap.put(k, v);
}
If you don't absolutely require that new object created is a HashMap you could create a new class (ideally extending AbstractMap<K, V>) representing a restricted view of the original Map. The class would have two private final fields
Map<? extends K, ? extends V> originalMap;
Set<?> restrictedSetOfKeys;
The get method for the new Map would be something like this
#Override
public V get(Object k) {
if (!restrictedSetOfKeys.contains(k))
return null;
return originalMap.get(k);
}
Notice that it is better if the restrictedSetOfKeys is a Set rather than a List because if it is a HashSet you would typically have O(1) time complexity for the get method.
This question already has answers here:
Sort a Map<Key, Value> by values
(64 answers)
Closed 6 years ago.
I need to sort my HashMap according to the values stored in it. The HashMap contains the contacts name stored in phone.
Also I need that the keys get automatically sorted as soon as I sort the values, or you can say the keys and values are bound together thus any changes in values should get reflected in keys.
HashMap<Integer,String> map = new HashMap<Integer,String>();
map.put(1,"froyo");
map.put(2,"abby");
map.put(3,"denver");
map.put(4,"frost");
map.put(5,"daisy");
Required output:
2,abby;
5,daisy;
3,denver;
4,frost;
1,froyo;
A generic version of a method to sort a Map resembles:
private static <K extends Comparable<K>, V extends Comparable<V>> Map<K, V> sort(
final Map<K, V> unsorted,
final boolean order) {
final var list = new LinkedList<>(unsorted.entrySet());
list.sort((o1, o2) -> order
? o1.getValue().compareTo(o2.getValue()) == 0
? o1.getKey().compareTo(o2.getKey())
: o1.getValue().compareTo(o2.getValue())
: o2.getValue().compareTo(o1.getValue()) == 0
? o2.getKey().compareTo(o1.getKey())
: o2.getValue().compareTo(o1.getValue()));
return list.stream().collect(
Collectors.toMap(
Entry::getKey, Entry::getValue, (a, b) -> b, LinkedHashMap::new
)
);
}
The following code offers ascending and descending sorting by value:
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 SortMapByValue
{
public static final boolean ASC = true;
public static final boolean DESC = false;
public static void main(String[] args)
{
// Creating dummy unsorted map
Map<String, Integer> unsortMap = new HashMap<String, Integer>();
unsortMap.put("B", 55);
unsortMap.put("A", 80);
unsortMap.put("D", 20);
unsortMap.put("C", 70);
System.out.println("Before sorting......");
printMap(unsortMap);
System.out.println("After sorting ascending order......");
Map<String, Integer> sortedMapAsc = sortByComparator(unsortMap, ASC);
printMap(sortedMapAsc);
System.out.println("After sorting descindeng order......");
Map<String, Integer> sortedMapDesc = sortByComparator(unsortMap, DESC);
printMap(sortedMapDesc);
}
private static Map<String, Integer> sortByComparator(Map<String, Integer> unsortMap, final boolean order)
{
List<Entry<String, Integer>> list = new LinkedList<Entry<String, Integer>>(unsortMap.entrySet());
// Sorting the list based on values
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());
}
}
});
// Maintaining insertion order with the help of LinkedList
Map<String, Integer> sortedMap = new LinkedHashMap<String, Integer>();
for (Entry<String, Integer> entry : list)
{
sortedMap.put(entry.getKey(), entry.getValue());
}
return sortedMap;
}
public static void printMap(Map<String, Integer> map)
{
for (Entry<String, Integer> entry : map.entrySet())
{
System.out.println("Key : " + entry.getKey() + " Value : "+ entry.getValue());
}
}
}
Using newer Java features:
import java.util.*;
import java.util.Map.Entry;
import java.util.stream.Collectors;
public class SortMapByValue
{
private static final boolean ASC = true;
private static final boolean DESC = false;
public static void main(String[] args)
{
// Creating dummy unsorted map
Map<String, Integer> unsortMap = new HashMap<>();
unsortMap.put("B", 55);
unsortMap.put("A", 20);
unsortMap.put("D", 20);
unsortMap.put("C", 70);
System.out.println("Before sorting......");
printMap(unsortMap);
System.out.println("After sorting ascending order......");
Map<String, Integer> sortedMapAsc = sortByValue(unsortMap, ASC);
printMap(sortedMapAsc);
System.out.println("After sorting descending order......");
Map<String, Integer> sortedMapDesc = sortByValue(unsortMap, DESC);
printMap(sortedMapDesc);
}
private static Map<String, Integer> sortByValue(Map<String, Integer> unsortMap, final boolean order)
{
List<Entry<String, Integer>> list = new LinkedList<>(unsortMap.entrySet());
// Sorting the list based on values
list.sort((o1, o2) -> order ? o1.getValue().compareTo(o2.getValue()) == 0
? o1.getKey().compareTo(o2.getKey())
: o1.getValue().compareTo(o2.getValue()) : o2.getValue().compareTo(o1.getValue()) == 0
? o2.getKey().compareTo(o1.getKey())
: o2.getValue().compareTo(o1.getValue()));
return list.stream().collect(Collectors.toMap(Entry::getKey, Entry::getValue, (a, b) -> b, LinkedHashMap::new));
}
private static void printMap(Map<String, Integer> map)
{
map.forEach((key, value) -> System.out.println("Key : " + key + " Value : " + value));
}
}
In Java 8:
Map<Integer, String> sortedMap =
unsortedMap.entrySet().stream()
.sorted(Entry.comparingByValue())
.collect(Collectors.toMap(Entry::getKey, Entry::getValue,
(e1, e2) -> e1, LinkedHashMap::new));
Assuming Java, you could sort hashmap just like this:
public LinkedHashMap<Integer, String> sortHashMapByValues(
HashMap<Integer, String> passedMap) {
List<Integer> mapKeys = new ArrayList<>(passedMap.keySet());
List<String> mapValues = new ArrayList<>(passedMap.values());
Collections.sort(mapValues);
Collections.sort(mapKeys);
LinkedHashMap<Integer, String> sortedMap =
new LinkedHashMap<>();
Iterator<String> valueIt = mapValues.iterator();
while (valueIt.hasNext()) {
String val = valueIt.next();
Iterator<Integer> keyIt = mapKeys.iterator();
while (keyIt.hasNext()) {
Integer key = keyIt.next();
String comp1 = passedMap.get(key);
String comp2 = val;
if (comp1.equals(comp2)) {
keyIt.remove();
sortedMap.put(key, val);
break;
}
}
}
return sortedMap;
}
Just a kick-off example. This way is more useful as it sorts the HashMap and keeps the duplicate values as well.
map.entrySet().stream()
.sorted((k1, k2) -> -k1.getValue().compareTo(k2.getValue()))
.forEach(k -> System.out.println(k.getKey() + ": " + k.getValue()));
You don't, basically. A HashMap is fundamentally unordered. Any patterns you might see in the ordering should not be relied on.
There are sorted maps such as TreeMap, but they traditionally sort by key rather than value. It's relatively unusual to sort by value - especially as multiple keys can have the same value.
Can you give more context for what you're trying to do? If you're really only storing numbers (as strings) for the keys, perhaps a SortedSet such as TreeSet would work for you?
Alternatively, you could store two separate collections encapsulated in a single class to update both at the same time?
package com.naveen.hashmap;
import java.util.*;
import java.util.Map.Entry;
public class SortBasedonValues {
/**
* #param args
*/
public static void main(String[] args) {
HashMap<String, Integer> hm = new HashMap<String, Integer>();
hm.put("Naveen", 2);
hm.put("Santosh", 3);
hm.put("Ravi", 4);
hm.put("Pramod", 1);
Set<Entry<String, Integer>> set = hm.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());
}
});
for (Entry<String, Integer> entry : list) {
System.out.println(entry.getValue());
}
}
}
As a kind of simple solution you can use temp TreeMap if you need just a final result:
TreeMap<String, Integer> sortedMap = new TreeMap<String, Integer>();
for (Map.Entry entry : map.entrySet()) {
sortedMap.put((String) entry.getValue(), (Integer)entry.getKey());
}
This will get you strings sorted as keys of sortedMap.
I extends a TreeMap and override entrySet() and values() methods. Key and value need to be Comparable.
Follow the code:
public class ValueSortedMap<K extends Comparable, V extends Comparable> extends TreeMap<K, V> {
#Override
public Set<Entry<K, V>> entrySet() {
Set<Entry<K, V>> originalEntries = super.entrySet();
Set<Entry<K, V>> sortedEntry = new TreeSet<Entry<K, V>>(new Comparator<Entry<K, V>>() {
#Override
public int compare(Entry<K, V> entryA, Entry<K, V> entryB) {
int compareTo = entryA.getValue().compareTo(entryB.getValue());
if(compareTo == 0) {
compareTo = entryA.getKey().compareTo(entryB.getKey());
}
return compareTo;
}
});
sortedEntry.addAll(originalEntries);
return sortedEntry;
}
#Override
public Collection<V> values() {
Set<V> sortedValues = new TreeSet<>(new Comparator<V>(){
#Override
public int compare(V vA, V vB) {
return vA.compareTo(vB);
}
});
sortedValues.addAll(super.values());
return sortedValues;
}
}
Unit Tests:
public class ValueSortedMapTest {
#Test
public void basicTest() {
Map<String, Integer> sortedMap = new ValueSortedMap<>();
sortedMap.put("A",3);
sortedMap.put("B",1);
sortedMap.put("C",2);
Assert.assertEquals("{B=1, C=2, A=3}", sortedMap.toString());
}
#Test
public void repeatedValues() {
Map<String, Double> sortedMap = new ValueSortedMap<>();
sortedMap.put("D",67.3);
sortedMap.put("A",99.5);
sortedMap.put("B",67.4);
sortedMap.put("C",67.4);
Assert.assertEquals("{D=67.3, B=67.4, C=67.4, A=99.5}", sortedMap.toString());
}
}
found a solution but not sure the performance if the map has large size, useful for normal case.
/**
* sort HashMap<String, CustomData> by value
* CustomData needs to provide compareTo() for comparing CustomData
* #param map
*/
public void sortHashMapByValue(final HashMap<String, CustomData> map) {
ArrayList<String> keys = new ArrayList<String>();
keys.addAll(map.keySet());
Collections.sort(keys, new Comparator<String>() {
#Override
public int compare(String lhs, String rhs) {
CustomData val1 = map.get(lhs);
CustomData val2 = map.get(rhs);
if (val1 == null) {
return (val2 != null) ? 1 : 0;
} else if (val1 != null) && (val2 != null)) {
return = val1.compareTo(val2);
}
else {
return 0;
}
}
});
for (String key : keys) {
CustomData c = map.get(key);
if (c != null) {
Log.e("key:"+key+", CustomData:"+c.toString());
}
}
}
package SortedSet;
import java.util.*;
public class HashMapValueSort {
public static void main(String[] args){
final Map<Integer, String> map = new HashMap<Integer,String>();
map.put(4,"Mango");
map.put(3,"Apple");
map.put(5,"Orange");
map.put(8,"Fruits");
map.put(23,"Vegetables");
map.put(1,"Zebra");
map.put(5,"Yellow");
System.out.println(map);
final HashMapValueSort sort = new HashMapValueSort();
final Set<Map.Entry<Integer, String>> entry = map.entrySet();
final Comparator<Map.Entry<Integer, String>> comparator = new Comparator<Map.Entry<Integer, String>>() {
#Override
public int compare(Map.Entry<Integer, String> o1, Map.Entry<Integer, String> o2) {
String value1 = o1.getValue();
String value2 = o2.getValue();
return value1.compareTo(value2);
}
};
final SortedSet<Map.Entry<Integer, String>> sortedSet = new TreeSet(comparator);
sortedSet.addAll(entry);
final Map<Integer,String> sortedMap = new LinkedHashMap<Integer, String>();
for(Map.Entry<Integer, String> entry1 : sortedSet ){
sortedMap.put(entry1.getKey(),entry1.getValue());
}
System.out.println(sortedMap);
}
}
public static TreeMap<String, String> sortMap(HashMap<String, String> passedMap, String byParam) {
if(byParam.trim().toLowerCase().equalsIgnoreCase("byValue")) {
// Altering the (key, value) -> (value, key)
HashMap<String, String> newMap = new HashMap<String, String>();
for (Map.Entry<String, String> entry : passedMap.entrySet()) {
newMap.put(entry.getValue(), entry.getKey());
}
return new TreeMap<String, String>(newMap);
}
return new TreeMap<String, String>(passedMap);
}
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map.Entry;
public class CollectionsSort {
/**
* #param args
*/`enter code here`
public static void main(String[] args) {
// TODO Auto-generated method stub
CollectionsSort colleciotns = new CollectionsSort();
List<combine> list = new ArrayList<combine>();
HashMap<String, Integer> h = new HashMap<String, Integer>();
h.put("nayanana", 10);
h.put("lohith", 5);
for (Entry<String, Integer> value : h.entrySet()) {
combine a = colleciotns.new combine(value.getValue(),
value.getKey());
list.add(a);
}
Collections.sort(list);
for (int i = 0; i < list.size(); i++) {
System.out.println(list.get(i));
}
}
public class combine implements Comparable<combine> {
public int value;
public String key;
public combine(int value, String key) {
this.value = value;
this.key = key;
}
#Override
public int compareTo(combine arg0) {
// TODO Auto-generated method stub
return this.value > arg0.value ? 1 : this.value < arg0.value ? -1
: 0;
}
public String toString() {
return this.value + " " + this.key;
}
}
}