1

I have this piece of code that filters from a list of objects based on a set of String identifiers passed in and returns a map of string-id and objects. Something similar to follows:

class Foo {
    String id;
    String getId() {return id};
};

// Get map of id --> Foo objects whose string are in fooStr
Map<String,Foo> filterMethod (Set<String> fooStr) {
    List<Foo> fDefs; // list of Foo objects
    Map<String,Foo> fObjMap = new HashMap<String, Foo>(); // map of String to Foo objects


    for (Foo f : fDefs) {
        if (fooStr.contains(f.getId()))
            fObjMap.put(f.getId(),f);
    }
    return (fObjMap);
}

Is there a better Java8 way of doing this using filter or map? I could not figure it out and tried searching on stackoverflow but could not find any hints, so am posting as a question.

Any help is much appreciated. ~Ash

4 Answers 4

2

Just use the filter operator with the same predicate as above and then the toMap collector to build the map. Also notice that your iterative solution precludes any possibility of key conflict, hence, I have omitted that, too.

Map<String, Foo> idToFooMap = fDefs.stream()
    .filter(f -> fooStr.contains(f.getId()))
    .collect(Collectors.toMap(Foo::getId, f -> f));
Sign up to request clarification or add additional context in comments.

1 Comment

This approach worked for me so accepting the answer.
2

When including items conditionally in the final output use filter and when going from stream to a map use Collectors.toMap. Here's what you end up with:

Map<String,Foo> filterMethod (final Set<String> fooStr) {
   List<Foo> fDefs; // list of Foo objects      
   return fDefs.stream()
        .filter(foo -> fooStr.contains(foo.getId()))
        .collect(Collectors.toMap(Foo::getId, Function.identity()));
}

4 Comments

Please look at comment to first reply. It suffers from same compilation error.
The error message was "error: local variables referenced from a lambda expression must be final or effectively final". This means you have to make the variable "fooStr" final, I updated the answer.
@ggreiner there is nothing to do with the extra final. It's already effectively final
@ash variables used in lambda should be final or effectively final, please check this: stackoverflow.com/questions/20938095/…
2

Though ggreiner has already provided a working solution, when there are duplicates you'd better handle it including a mergeFunction.

Directly using Collectors.toMap(keyMapper, valueMapper), one or another day you will encounter this following issue.

If the mapped keys contains duplicates (according to Object.equals(Object)), an IllegalStateException is thrown when the collection operation is performed. If the mapped keys may have duplicates, use toMap(Function, Function, BinaryOperator) instead.

Based on the OP's solution, I think it would be better using

import static java.util.stream.Collectors.*; // save some typing and make it cleaner;

fDefs.stream()
     .filter(foo -> fooStr.contains(foo.getId()))
     .collect(toMap(Foo::getId, foo -> foo, (oldFoo, newFoo) -> newFoo));

Comments

0

Maybe something like this?

Map<String,Foo> filterMethod (Set<String> fooStr) {
    List<Foo> fDefs; // get this list from somewhere
    Map<String, Foo> fObjMap = new HashMap<> (); 

    fDefs.stream()
        .filter(foo -> fooStr.contains(foo.getId()))
        .forEach(foo -> fObjMap.put(foo.getId(), foo))

    return fObjMap;
}

2 Comments

I get compilation error at the line: "error: local variables referenced from a lambda expression must be final or effectively final" at the fooStr.contains(foo.getId()))
The compilation error can be resolved by doing something like fDef.stream().map(foo -> foo.GetId()).filter(fooStr::contains)) But not sure how to get the foo object in forEach??

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.