Nidhi257
Nidhi257

Reputation: 977

How to get sorted a TreeMap from list using streams

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

Answers (3)

Holger
Holger

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

Marc Nuri
Marc Nuri

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

Paul Boddington
Paul Boddington

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 Integers?

Map<Integer, Set<String>> result = new TreeMap<>();
for (String name : names)
    result.computeIfAbsent(getLevel(name).levelId, k -> new HashSet<>()).add(name);   

Upvotes: 1

Related Questions