How to collect a stream into a TreeMap
Asked Answered
I

3

23

I know this is a noob a question, but I couldn't find a simpe answer anywhere else. Question is: I need to write a method that returns a SortedMap, so a tree map should work just fine. I have a HashMap< String, Skill>, the Skill class has both the methods getName and getNumApplicants and I need to return a SortedMap<String, Long>, with the name of the skill as a key and the number of applicants as value. This is where I stand:

private Map<String,Skill> skillMap = new HashMap<>();

public SortedMap<String, Long> skill_nApplicants() {

    return skillMap.values().stream().collect(...);
}

This is the Skill class

public class Skill {

    private String name;
    private List <Position> reqPosition = new ArrayList<>();
    private Long numApplicants;

    public void plusOneApplicant() {
        this.numApplicants++;
    }

    public Long getNumApplicants() {
        return numApplicants;
    }
    public Skill(String name) {
        super();
        this.name = name;
        this.numApplicants = 0L;
    }
    public String getName() {
        return name;
        }

    public List<Position> getPositions() {
        return reqPosition;
        }
    public void addReqPosition(Position p) {
        this.reqPosition.add(p);
        return;
    }
}

I know this should be very easy, I just have a very hard time in understanding this all thing.

Inherent answered 1/9, 2018 at 15:23 Comment(5)
Quick question, what is the key of your skillMap map? You say that you have a HashMap<Skill> which is not correct. Also please post some more code.Lacunar
skillMap<String,Skill>, this is the complete type of the map.Inherent
And they key? What does your key represent?Lacunar
Possible duplicate of #31661400 ?Lannylanolin
Similar question, but in that case the solution involves returning a map of maps, I only need to insert a single valueInherent
E
42

Don't collect the data to a HashMap first, then convert to a TreeMap. Collect the data directly to a TreeMap by using the overloaded toMap(keyMapper, valueMapper, mergeFunction, mapSupplier) method that allows you to specify which Map to create (4th parameter).

public SortedMap<String, Long> skill_nApplicants() {
    return skillMap.values().stream().collect(Collectors.toMap(
            Skill::getName,
            Skill::getNumApplicants,
            Math::addExact, // only called if duplicate names can occur
            TreeMap::new
    ));
}
Ethyl answered 1/9, 2018 at 16:26 Comment(2)
Is there a function that omits the merger function but specifies the map supplier?Inherent
@francescofoschi No. Which you could see for yourself in the javadoc: docs.oracle.com/javase/8/docs/api/java/util/stream/…Ethyl
P
3

This is how you can do it

public SortedMap<String, Long> skill_nApplicants(Map<String, Skill> skillMap) {
    Map<String, Long> result = skillMap.values().stream().collect(Collectors.toMap(Skill::getName, Skill::getNumApplicants));
    return new TreeMap<>(result);
}
Pithead answered 1/9, 2018 at 15:45 Comment(8)
I'll give it a try, maybe I'll change it to public SortedMap<String, Long> skill_nApplicants(Map<String, Skill> skillMap) { Map<String, Long> result = skillMap.values().stream().sorted(comparing(Skill::getName)).collect(Collectors.toMap(Skill::getName, Skill::getNumApplicants)); return new TreeMap<>(result); }Inherent
If name or numApplicants values are null, you will get NullPointerExceptionPithead
I tried your solution in eclipse and it does not allow the cast to TreeMapInherent
You should use JDK8, I have test it locally before sending you the answer and it is working. Not need to cast.Pithead
I have JDK8 running this is the error : "Exception in thread "main" java.lang.ClassCastException: java.util.HashMap cannot be cast to java.util.TreeMap at applications.HandleApplications.skill_nApplicants(HandleApplications.java:140)"Inherent
I omitted the "new" after return, now it works, thanksInherent
You're welcome. Select the answer as valid answer if you can, thanks.Pithead
you're constructing 2 maps with this one, see the best answer for collecting directly into the treemapDissimulation
R
2

If, in your stream, you don't have two (or more) values which should be mapped with the same key, then you can avoid to use a Collector at all (and thus you don't need to think about a merge function).

All you need to do is to simply add each skill to the map with a forEach:

public SortedMap<String, Long> skill_nApplicants() {
    Map<String, Long> result = new TreeMap<>();
    
    skillMap.values()
        .forEach((skill) -> result.put(skill.getName(), skill.getNumApplicants());
        
    return result;
}

You can wrap result with Collections.unmodifiableSortedMap if you want to return an unmodifiable map.

Rose answered 29/6, 2021 at 13:12 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.