Reputation: 977
I have a class say Level (its just a dummy class for understanding).
I want a sorted TreeMap<Level,Set<String>>
based on levelId.
Please find below code
import java.util.*;
import java.util.stream.Collectors;
public class Level {
int levelId;
public Level(int levelId) {
this.levelId = levelId;
}
public static Level getLevel(String name){
return new Level(name.length());
}
public static void main(String[]args){
Set<String> names=new HashSet<>();
names.add("Mahesh");
names.add("Ram");
names.add("Rita");
Map<Level, Set<String>> map = names.stream().collect(
Collectors.groupingBy(name->Level.getLevel(name),
Collectors.mapping(name->name,Collectors.toSet())));
}
}
I have also tried with Collectors.collectingAndThen()
.
Any help is appreciated.
Thanks in advance.
Upvotes: 11
Views: 15994
Reputation: 298399
If you don’t want to let Level
implement Comparable
, you need a Comparator
. Then, you have to pass a lambda expression creating a TreeMap
using this separator as map factory to the groupingBy
collector:
public class Level {
int levelId;
public Level(int levelId) {
this.levelId = levelId;
}
public static Level getLevel(String name){
return new Level(name.length());
}
public int getLevelId() {
return levelId;
}
public static void main(String[]args){
Set<String> names=new HashSet<>();
names.add("Mahesh");
names.add("Ram");
names.add("Rita");
Comparator<Level> c = Comparator.comparingInt(Level::getLevelId);
Map<Level, Set<String>> map = names.stream()
.collect(Collectors.groupingBy(
Level::getLevel, () -> new TreeMap<>(c), Collectors.toSet()));
}
}
Upvotes: 17
Reputation: 3664
Your modified working code would look like this, please see @4castle comments:
public class Level implements Comparable<Level> {
int levelId;
public Level(int levelId) {
this.levelId = levelId;
}
@Override
public int compareTo(Level o) {
return Integer.compare(levelId, o.levelId);
}
public static Level getLevel(String name){
return new Level(name.length());
}
public static void main(String[]args){
Set<String> names=new HashSet<>();
names.add("Mahesh");
names.add("Ram");
names.add("Rita");
Map<Level, Set<String>> map = names.stream().collect(
Collectors.groupingBy(Level::getLevel, TreeMap::new,
Collectors.toSet()));
}
}
Upvotes: 6
Reputation: 37655
In my opinion, using streams improves readability only in the simplest cases. If you have specific requirements, such as you need a specific Map
implementation, or you need to use a custom Comparator
, I would strongly encourage using a for loop instead. Yes, you could search the Collectors
class for an appropriate method, but I believe the resulting code is harder to follow and less flexible if you need to make another small change later.
In Java 8, there were many improvements to the Map
interface which mean doing this kind of thing in a loop is now much less painful than had previously been the case.
Map<Level, Set<String>> result = new TreeMap<>(Comparator.comparingInt(level -> level.levelId));
for (String name : names)
result.computeIfAbsent(getLevel(name), k -> new HashSet<>()).add(name);
In this case, I am not sure why you want a map with Level
keys anyway. Since you want to group by id, wouldn't it make more sense for the keys to be Integer
s?
Map<Integer, Set<String>> result = new TreeMap<>();
for (String name : names)
result.computeIfAbsent(getLevel(name).levelId, k -> new HashSet<>()).add(name);
Upvotes: 1