I did not find needed information, so I decided to create a new question.
I have a little test app where I want to sort my Map by Values. But I can't understand why I can't do it in the following way:
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Map;
public class Test {
public int test(int [] array) {
Map<Integer, Integer> map = new HashMap<>();
map.put(1,4);
map.put(2,3);
map.put(5,1);
map.put(7,0);
map.put(4,4);
map.put(9,1);
Collections.sort(map.entrySet(), new Comparator<Map.Entry<Integer, Integer>>() {
#Override
public int compare(Map.Entry<Integer, Integer> t, Map.Entry<Integer, Integer> t1) {
return t.getValue().compareTo(t1.getValue());
}
});
for(Map.Entry<Integer, Integer> entry : map.entrySet()){
sum += entry.getValue();
}
return sum;
}
}
and Main class:
public class Main {
public static void main(String[] args) {
Test test = new Test();
System.out.println(test.test(arr));
}
}
This app shoud return 14 in this case. But I have this message on
Collections.sort(...) part:
sort (java.util.List, java.util.Comparator) in Collections cannot be applied to
(java.util.Set>,
anonymous
java.util.Comparator>)
reason: no instance(s) of type variable(s) T exist so that
Set> conforms to List
but if I change it to Collections.min(...) or Collections.max(...):
Collections.min(map.entrySet(), new Comparator<Map.Entry<Integer, Integer>>() {
#Override
public int compare(Map.Entry<Integer, Integer> t, Map.Entry<Integer, Integer> t1) {
return t.getValue().compareTo(t1.getValue());
}
});
there will be no issues.
Java Map can't be sorted by value. But you can create a list from Map.entrySet() or maybe you don't need a collection at all.
Using List and Comparator.
List<Map.Entry<Integer, Integer>> list = new ArrayList<>(map.entrySet());
list.sort(Comparator.comparing(Map.Entry::getValue));
Using Stream
map.entrySet().stream()
.sorted(Comparator.comparing(Map.Entry::getValue))
//do something here
Related
Trying to sort HashMap of <String,Double> without using treeMap or other method. Need the code to pass a hashMap and return a sorted hashMap in the fastest time. What am I doing wrong with the string Comparator. Please look and advise. Thank you very much!!!
/// Here's the main
package Sort_String_Double_without_TreeMap;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
public class Main {
public static void main(String[] args) throws InterruptedException {
Map<String, Double> outGoing = new HashMap<>();
outGoing.put("J", -5.0);
outGoing.put("X", 0.7);
outGoing.put("C", 0.0);
outGoing.put("D", 80.0);
outGoing.put("A", 80.0);
System.out.println("---UNSORTED---");
System.out.println(outGoing);
TimeUnit.SECONDS.sleep(1);
Helper_SorterClass sorter = new Helper_SorterClass ();
System.out.println("---SORTED---");
System.out.println();
System.out.println("SIZE= " + sorter.SortHashMapKey(outGoing).size());
System.out.println(sorter.SortHashMapKey(outGoing));
}
}
And Here's the other class...
/// Here's the helper class
package Sort_String_Double_without_TreeMap;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class Helper_SorterClass {
public Map<String, Double> SortHashMapKey(Map<String, Double> unsortedMap) {
List<String> list = new ArrayList(unsortedMap.keySet());
Collections.sort(list, new Comparator<String>() {
#Override
public int compare(String o1, String o2) {
return String.valueOf(o1).compareTo(String.valueOf(o2));
}
});
Map<String, Double> sortedMap = new HashMap<>();
for (String keys : list) {
sortedMap.put(keys, unsortedMap.get(keys));
}
return sortedMap;
}
}
Unfortunately getting wrong output.
run:
---UNSORTED---
{A=80.0, C=0.0, D=80.0, X=0.7, J=-5.0}
---SORTED---
SIZE= 5
{A=80.0, C=0.0, D=80.0, X=0.7, J=-5.0}
BUILD SUCCESSFUL (total time: 1 second)
Map<String, Double> sortedMap = new HashMap<>();
for (String keys : list) {
sortedMap.put(keys, unsortedMap.get(keys));
}
you should use LinkedHashMap instead of HashMap as below:
Map<String, Double> sortedMap = new LinkedHashMap<>();
for (String keys : list) {
sortedMap.put(keys, unsortedMap.get(keys));
}
HashMap doesn't maintain insertion order. So once you put the entries in a new HashMap() after comparing, it will again give unsorted result when you iterate over the new map. But if you use a LinkedHashMap, it will maintain insertion order while iterating.
I have a usecase where client is sending a List<Function>. Task is to iterate and execute this function and keep it in a TypedSafeMap.
pseudo client code:
Function<String, Integer> firstFn = x -> x.length();
Function<String, String> secondFn = x -> x.substring(0);
client.runTheseFunctions(Arrays.asList(firstFn, secondFn));
Inside runtTheseFunctions in the code, task is to execute these functions and keep it in a TypedSafeMap where the key is the datatype of the type of the result of the function and value is the return of functions.apply();
The code below
public static void runTheseFunctions(List<Function<Employee, ?>> lst, Employee o) {
lst.stream().forEach( x -> {
typedSafeMap.put(????, x.apply(o));
//The key is nothing but the datatype of the x.apply(o).
//How do I add this in runtime here. Generics is all compile time safety.
});
}
public static void runTheseFunctions(List<Function<Employee, ?>> lst, Employee o) {
lst.stream().collect(Collectors.toMap(f -> f.apply(o).getClass(), f -> f.apply(o)));
}
You can implement your "runTheseFunctions" method as shown below:
public static void runTheseFunctions(List<Function<Employee, ?>> lst, Employee o) {
Map<Class<?>, Object> typedSafeMap = new HashMap<>();
lst.stream().forEach(x -> {
Object value = x.apply(o);
typedSafeMap.put(value.getClass(), value);
});
System.out.println(typedSafeMap);
}
In case the List of Functions contains two or more Functions with the same outputtype (for instance: String getFirstName, String getLastName, toMap will fail. So an alternative is:
var map = list.stream().collect(groupingBy(
f -> f.apply(e).getClass(),
mapping(f -> f.apply(e), toList())
));
Here is an example of what you want to achieve, and you can use for your tests. I assumed an trivial implementation of Employee class, just to give you an idea:
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
class Employee {
String name;
public Employee(String name) {
this.name = name;
}
public int length() {
return name.length();
}
public String substring(int index) {
return name.substring(index);
}
}
public class Test {
public static void main(String[] args) {
Employee e = new Employee("Marco");
Function<Employee, Integer> firstFn = x -> x.length();
Function<Employee, String> secondFn = x -> x.substring(0);
runTheseFunctions(Arrays.asList(firstFn, secondFn), e);
}
public static void runTheseFunctions(List<Function<Employee, ?>> lst, Employee o) {
Map<Class, Object> typedSafeMap = new HashMap<>();
lst.stream().forEach(x -> {
Object result = x.apply(o);
typedSafeMap.put(x.apply(o).getClass(), x.apply(o));
// The key is nothing but the datatype of the x.apply(o).
// How do I add this in runtime here. Generics is all compile time safety.
});
typedSafeMap.entrySet().forEach(entry -> System.out.println(entry.getKey() + " - " + entry.getValue()));
}
}
And here is the output:
class java.lang.String - Marco
class java.lang.Integer - 5
Enhancing #Yonas answer:
private static Map<?, ? extends Object> runTheseFunctions(List<Function<String, ? extends Object>> list, String o) {
return list.stream()
.map(f -> f.apply(o))
.collect(Collectors.toMap(result -> result.getClass(), Function.identity()));
}
This will call the f.apply(o) only once.
I have
List<Gift> gifts = new ArrayList<>();
gifts .add(new Gift().withType(INF, CHD));
gifts .add(new Gift().withType(ADT, CHD));
gifts .add(new Gift().withType(INF, ADT));
Gift has a method List<Type> getTypes();
and now I'd like to transform gifts list into something like
Map<Type,List<Gift>>.
I'd like to do it with Java 8 and lambdas in one line. Is it possible?
public class Gift {
public List<Type> getTypes() {
return types;
}
public Gift withType(Type... types) {
this.types = Arrays.asList(types);
return this;
}
List<Type> types = new ArrayList<>();
}
public enum Type {
ADT,
CHD,
INF;
}
Previous old code (it looks awfully). That's all what I have.
Map<Type, List<Gift>> byTypes = new HashMap<>();
for (Gift gift : gifts) {
for (Type type : gift.getTypes()) {
List<Gift> giftList = byTypes.get(type);
if (giftList == null) {
giftList = new ArrayList<>();
}
giftList.add(gift);
byTypes.put(type,giftList);
}
}
Using Guava's Multimap:
ListMultimap<Type, Gift> multimap = ArrayListMultimap.create();
gifts.forEach(g -> g.getTypes().forEach(t -> multimap.put(t, g)));
Map<Type, Collection<Gift>> map = multimap.asMap();
Ok, I found solution which satify me :-) I wrote my collector :-D
Map<Type, List<Gift>> collect1 = gifts.stream().collect(new TypeToManyGiftCollector());
public class TypeToManyGiftCollector
implements Collector<Gift, Map<Type, List<Gift>>, Map<Type, List<Gift>>> {
#Override
public Supplier<Map<Type, List<Gift>>> supplier() {
return () -> new HashMap<Type, List<Gift>>() {{
for (Type type : Type.values()) {
put(type, new ArrayList<Gift>());
}
}};
}
#Override
public BiConsumer<Map<Type, List<Gift>>, Gift> accumulator() {
return (Map<Type, List<Gift>> map, Gift gift) -> {
gift.getTypes().stream().forEach(type -> map.get(type).add(gift));
};
}
#Override
public BinaryOperator<Map<Type, List<Gift>>> combiner() {
return (Map<Type, List<Gift>> map1, Map<Type, List<Gift>> map2) ->
{
for (Type type : Type.values()) {
map1.get(type).addAll(map2.get(type));
}
return map1;
};
}
#Override
public Function<Map<Type, List<Gift>>, Map<Type, List<Gift>>> finisher() {
return Function.identity();
}
#Override
public Set<Characteristics> characteristics() {
return Collections.unmodifiableSet(EnumSet.of(IDENTITY_FINISH));
}
}
Reintroducing your intermediate Pair class P, but in a slightly different way. I think we might expect that in later iterations of Java, this is going to become much more common place and easier to do with the introduction of a built-in Pair type, and the implementation of Value Objects.
package play;
import java.io.IOException;
import java.util.ArrayList;
import java.util.EnumMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import static play.Type.*;
public class Play {
static class P<K,V> { K k; V v; P(K kk,V vv) {k=kk;v=vv;}}
public static void main(String[] args) throws IOException {
List<Gift> gifts = new ArrayList<>();
gifts .add(new Gift().withType(INF, CHD));
gifts .add(new Gift().withType(ADT, CHD));
gifts .add(new Gift().withType(INF, ADT));
Map<Type,List<Gift>> m = gifts.stream()
.flatMap((g)->g.getTypes().stream().map((t)->new P<>(t,g)))
.collect(
Collectors.groupingBy(
(p)->p.k,
// Does not work with <code>new EnumMap<>(Type.class)</code>
// I dont know why, ...
()->new EnumMap(Type.class),
Collectors.mapping(
(p)->p.v,
Collectors.toList()
)
)
);
System.out.println("Map: "+m.toString());
}
}
My only problem in this code is that I cannot explain the need for a 'Untyped' Map as the Map factory. Feel free to explain if you know why ...
I found other solution with a reduce method. In my opinion smaller one and easier to understand.
HashMap<Type, List<Gift>> result = gifts.stream().reduce(
new HashMap<Type, List<Gift>>() {{
asList(Type.values()).stream().forEach(type -> put(type, new ArrayList<>()));
}}
,
(map, gift) -> {
gift.getTypes().stream().forEach(type -> map.get(type).add(gift));
return map;
}
,
(map1, map2) -> {
asList(Type.values()).stream().forEach(type -> map1.get(type).addAll(map2.get(type)));
return map1;
}
);
I'm having trouble applying sorting mechanism through my application.
Reason was sometimes sort are not accurate and also the comparator thing in java still not clear for me, but i have used sort here and there.
Now, current problem is as follows.
I have
HashMap<String, ModelX.ContactModel> unsortedModelContacts =
new HashMap<String,ModelX.ContactModel>(contacts.size());
After that I fached
contactlist and using for loop I have put the values as follows:
unsortedModelContacts.put(stringvalue, modelContact);
//object having name , and other details
How can I sort the unsortedModelContacts sorting modelContact.getName property?
If your map's key is different from the name field then you can consider using this approach. Writing a separate comparator
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;
import sample.ModelX.ContactModel;
public class SortMapSample {
public static void main(String[] args) throws Exception {
Map<String, ModelX.ContactModel> unsortedModelContacts = new HashMap<String,ModelX.ContactModel>(10);
unsortedModelContacts.put("1", new ModelX.ContactModel("James"));
unsortedModelContacts.put("2", new ModelX.ContactModel("Mary"));
unsortedModelContacts.put("3", new ModelX.ContactModel("John"));
unsortedModelContacts.put("4", new ModelX.ContactModel("Amanda"));
unsortedModelContacts.put("5", new ModelX.ContactModel("Charles"));
System.out.println(sortMap(unsortedModelContacts));
}
private static Map<String, ModelX.ContactModel> sortMap(
Map<String, ModelX.ContactModel> unsortedMap) {
List<Entry<String, ModelX.ContactModel>> list = new LinkedList<Entry<String, ModelX.ContactModel>>(
unsortedMap.entrySet());
Collections.sort(list,
new Comparator<Entry<String, ModelX.ContactModel>>() {
#Override
public int compare(Entry<String, ContactModel> o1,
Entry<String, ContactModel> o2) {
return o1.getValue().getName().compareTo(o2.getValue().getName());
}
});
Map<String, ModelX.ContactModel> sortedMap = new LinkedHashMap<String, ModelX.ContactModel>();
for(Entry<String, ModelX.ContactModel> item : list){
sortedMap.put(item.getKey(), item.getValue());
}
return sortedMap;
}
}
SortedMap<String,ModelX.ContactModel> sortedModelContacts = new TreeMap<>();
for( ModelX.ContactModel modelContact: contactlist ){ // same list as before
sortedModelContacts.put( modelContact.getName(), modelContact);
}
You can now access entries of this map in sort order of the name property.
Note: this assumes that names are unique. If this isn't true, you'll have to use a multimap or
Map<String,ModelX.Set<ContactModel>>
and modify the put and other accesses accordingly.
I have a list of maps
List<Map<String, Object>> people = new ArrayList<Map<String,Object>>();
that gets populated like so
map.put("firstName",John);
map.put("lastName",Smith);
map.put("type","1"); //type is either 1 or a 0
people.add(map);
and what I want to do with this list after it gets populated is so have all the people as type 0 at the top of the list and all with type 1 at the bottom.
I know I need to use a Comparator but I have never used one before so I dont know how to use one or how it works.
Could someone help me out
Like this
Collections.sort( people, new Comparator<Map<String, Object>>() {
#Override
public int compare( Map<String, Object> o1, Map<String, Object> o2 ) {
return (Integer.parseInt((String)o1.get( "type" ))) -
(Integer.parseInt((String)o2.get( "type" )));
}
} );
However, there are many ways to make this better. If you cannot use a Person object to represent the map as suggested by #Pshemo, then at least, use a reasonable data type for your type attribute. The best would be an enum:
public enum PersonType {
TYPE_1, TYPE_2
}
Then the comparisons are much cleaner and faster and much more readable.
Comparator is just an interface that needs to be implemented, it contains only one method that needs to be overriden.
For example:
List<Map<String, Object>> people = new ArrayList<Map<String,Object>>();
Map<String, Object> map = new HashMap<String, Object>();
map .put("firstName","John");
map.put("lastName","Smith");
map.put("type","1"); //type is either 1 or a 0
people.add(map);
Collections.sort(people, new Comparator<Map<String, Object>>() {
#Override
public int compare(Map<String, Object> o1, Map<String, Object> o2) {
// you may compare your map here
return 0;
}
});
Try this
Collections.sort(people, new Comparator<Map<String, String>>() {
#Override
public int compare(Map<String, String> m1, Map<String, String> m2) {
return m1.get("type").compareTo(m2.get("type"));
}
});
You can try like this :
class ListByType
{
private static class MyComparator implements Comparator<HashMap<String,String>>
{
#Override
public int compare(HashMap mp1 , HashMap mp2)
{
return ((String)(mp1.get("type")).compareTo((String)mp2.get("type"));
}
}
public static void main(String[] args)
{
List<Map<String, String>> people = new ArrayList<Map<String,String>>();
HashMap<String,String> map = new HashMap<String,String>();
map.put("firstName","John");
map.put("lastName","Smith");
map.put("type","1"); //type is either 1 or a 0
people.add(map);
/*...
..
...
Add more maps here..
*/
//Sort the list
Collections.sort(people,new MyComparator());
}
}