Java generics incompatible types (no instance(s) of type variable(s) exist)

Viewed 14826

The question title may seem to be same as some other post but the content is different. So please don't mark it duplicate.

Problem:

I have the below class:

public class SCDTO extends RDTO {
    private List<String> sCPairs = Collections.emptyList();

    public SCDTO(List<String> sCPairs) {
        this.sCPairs = sCPairs;
    }

    //Getter setter
    
}

I am trying to using the below lambda expression to set the sCPairs.

sCPairsObject.setSCPairs(
         util.getSCMap().entrySet().stream()
        .filter(entry -> entry.getValue().contains("abc"))
        .collect(Collectors.toCollection(ArrayList<String>::new))
);

But I have an compilation error saying:

no instance(s) of type variable(s) exist so that Entry<String, List<String>> conforms to String

util.getSCMap returns Map<String, List<String>>.

Can anyone please explain why this is happening and how to solve it?

Thanks.

4 Answers

You are streaming entries from the map:

sCPairsObject.setSCPairs(util.getSCMap().entrySet().stream()

Then filtering out some of them:

.filter(entry -> entry.getValue().contains("abc"))

now you need to map the entries to List, for example:

.map(entry -> entry.getValue())

and stream the contents of all these lists as one stream:

.flatMap(List::stream)

Finally collect the values into a list:

.collect(Collectors.toList());

Your stream pipeline finds all the Map entries whose value List<String> contains the String "abc", and tries to collect them into a List<String>.

You didn't specify how you intend to convert each Map.Entry<String,List<String>> element that passes the filter into a String. Depending on the required logic, perhaps you are missing a map() step after the filter.

For example, if you wish to collect all the keys having a value that passes the filter into a List<String>:

 sCPairsObject.setSCPairs(util.getSCMap()
                              .entrySet()
                              .stream()
                              .filter(entry -> entry.getValue().contains("abc"))
                              .map(Map.Entry::getKey)
                              .collect(Collectors.toCollection(ArrayList<String>::new)));

You don't need to iterate over the entrySet since you are not using the key at all. You can get the values of the Map and filter that. Looks like this

sCPairsObject.setSCPairs(util.getSCMap()
   .values()
   .stream()
   .filter(entry -> entry.contains("abc"))
   .flatMap(List::stream)
   .collect(Collectors.toList());

Since you want to convert a Map<String, List<String>> to a List<String> where the list should be a union of all matching value lists you'll need a flatMap() to join the streams on those value lists into a single stream.

Additionally you don't seem to need the keys so just stream on the map's values:

List<String> scPairs = util.getSCMap().values().stream() //stream on the values only
                           .filter( l -> l.contains( "abc" ) ) //filter the lists
                           .flatMap( Collection::stream ) //join the individual streams
                           .collect( Collectors.toList() ); //collect the result into a single list
Related