Sayantan
Sayantan

Reputation: 605

Java 8 : Compare 2 Collections and formulate a separate collection using another property

I have a very simple code snippet that I would like to be converted to Java 8's list streaming methods. Any help would be really appreciated.

I have 2 collections one is a Set of String and other a list of a simple POJO which itself consists a list of string. Below is the relevant code:

POJO sample

public class SimplePojo {

    private long id;
    private String name;
    private List<String> desiredStringList;

    public long getId() {
        return id;
    }

    public void setId(long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public List<String> getDesiredStringList() {
        return desiredStringList;
    }

    public void setDesiredStringList(List<String> desiredStringList) {
        this.desiredStringList= desiredStringList;
    }

}

Comparison that I am wishing to transform

Set<String> someStrings // Contains a list of strings that needs to be compared

List<SimplePojo> pojoObjectList // Contains list of SimplePojo objects 

/* Each of someStrings need to be compared to each of SimplePojo.name property
    and corresponding actions which is List object needs to be populated as a separate list
    Below is the code snippet which has a mixture of Java 8 stream and regular For-Each loop
*/
Set<String> desiredStrings = new HashSet<String>();

for(String s : someStrings) {
    List<String> interimDesiredStrings =  pojoObjectList.stream()
                                            .filter(o -> StringUtils.equals(s, o.getName()))
                                            .flatMap(o -> o.getDesiredStringList().stream())
                                            .collect(Collectors.toList());

    desiredStrings.addAll(interimDesiredStrings);
}

Upvotes: 1

Views: 1023

Answers (2)

Holger
Holger

Reputation: 298103

Well, this perfectly demonstrates, where overusing third-party utility functions leads to.

You are using StringUtils.equals and there’s no sign of whether this is the Apache Commons version or the Spring version (or yet another library having such a function). Or why you are using this special method.

In the Apache case, it would be justified, if it is at least version 3 and getName() is declared to return CharSequence, in the Spring case, it’s not justified at all. Besides supporting CharSequence in the Apache case, both variants merely exist to handle null silently, but if handling null really is your concern, using the standard Objects.equals instead would immediately clarify the intention. Perhaps, even null isn’t you concern.

The reason, why this is so important, is that your task can be easily implemented using standard equality semantics

Set<String> desiredStrings = pojoObjectList.stream()
    .filter(o -> someStrings.contains(o.getName()))
    .flatMap(o -> o.getDesiredStringList().stream())
    .collect(Collectors.toSet());

Assuming that someStrings refers to a Set with a reasonable lookup efficiency (as with most Set implementations), this will be significantly more efficient than your nested iterations.

Note that if the Set referenced by someStrings does not support null, it would imply that a null name is not “desired”, hence, if getName() could return null, changing the filter to

    .filter(o -> o.getName()!=null && someStrings.contains(o.getName()))

would solve this in a clear way.

Upvotes: 2

Misha
Misha

Reputation: 28133

Why not go over the list of objects once and just check if the name is in the set of names to match?

Set<String> desiredStrings = pojoObjectList.stream()
    .filter(o -> someStrings.contains(o.getName()))
    .flatMap(o -> o.getDesiredStringList().stream())
    .collect(toSet());

Upvotes: 1

Related Questions