I have function which returns a Map<String, Set<String>>, Code before java 8:

Map<String, Set<String>> degreeMap = new HashMap<>();
for(Course  course : courses){
    Set<String> cList = degreeMap.get(course.getCourseLevel().toString());
        cList.addAll(course.getMasterDegree()); //this is what i want to append to the old set
        degreeMap.put(course.getCourseLevel().toString(), cList);
    } else{
        degreeMap.put(course.getCourseLevel().toString(), new HashSet<>(course.getMasterDegree()));
return degreeMap;

Which return a map of courselevel -> set of degrees.

For example, it read all the courses and return a map like:

{"undergraduate" : ["BTech", "BSc", "BE"],
"masters": ["MTech", "MBA"],
"Executive": ["PGDBM", "EECP"]}

Here is my Course class:

public class Course {
    private List<String> masterDegree;
    private CourseLevel courseLevel;

But I want to write this piece of code in Java 8 style. For that, I tried this:

Map<String, Set<String>> degreeMap = courses.stream().collect(
        Collectors.groupingBy(c -> c.getCourseLevel().toString(),
                Collectors.mapping(c -> c.getMasterDegree(), Collectors.toSet()))

which is not working and I am getting the following compile-time error on this:

no instance(s) of type variable(s) exist so that List conforms to String inference variable T has incompatible bounds: equality constraints: String lower bounds: List

Any suggestion, how to achieve this?

  • It sounds to me that you're looking for a flatMapping equivalent of Java-9 in an older version. – Naman Apr 12 at 9:12
  • 1
    @GhostCat tried editing the question to be relevant enough. hope its clear now. – Naman Apr 12 at 9:35
  • 1
    @Naman thanks for resolving this battle :P – KayV Apr 12 at 9:35
  • 1
    for(Course course : courses) degreeMap.computeIfAbsent(course.getCourseLevel() .toString(), x -> new HashSet<>()) .addAll(course.getMasterDegree()); By the way, even in your pre-Java 8 version, there never was a reason to put the same list into the Map again after addAll. – Holger Apr 12 at 12:27

Not tested, but looks like, you're looking for something like:

    return courses.stream()
            .collect(Collectors.toMap(course -> course.getCourseLevel().toString(),
                    course -> new HashSet<>(course.getMasterDegree()),
                    (set1, set2) -> Stream.of(set1, set2)
  • 2
    In this case, (set1, set2) -> { set1.addAll(set2); return set1; } clearly is the preferable merge function… – Holger Apr 12 at 12:30

I don't think you need lamda expression, you should refactor your codes to make it clear instead.

        // supposed to be initialied with non-empty values
        // Map<String, Set<String>> degreeMap = ...

        for(Course  course : courses){
            // something like below
            String key = course.getCourseLevel().toString();
            // degreeSet should not be null
            Set<String> degreeSet = course.getMasterDegree();

            // TODO: check nullable

            degreeMap.put(key, degreeSet);

        return degreeMap;
  • This is what i already did. – KayV Apr 12 at 9:24

You might be interested in the Collectors.toMap() method.

Here is an example that you may need to tweak as I did not test it.

Map<String, Set<String>> degreeMap = courses.stream()
            item -> item.getCourseLevel().toString(), //your key mapping
            item -> item.getMasterDegree(), //your value mapping
            (oldValue, newValue) -> { //your process of aggregation
                if (null == oldValue) {
                    return newValue;
                } else {
                    return oldValue;
            LinkedHashMap::new //your result initialiser

Also, another tip: you do not need to get by key and check for null, you can use the .compute(), .computeIfAbsent(), .computeIfPresent() methods on the map

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.