Search code examples
javalistfilterjava-stream

Stream on objects list that cointains a child list need to be filtered Java Stream


I would like to ask you what was the most elegant and performant way to execute the following piece of code:

return servizioGenerico.ottieniCall(prefixCall).getListaAttivita().stream().parallel()
            .peek(attivita -> {
                attivita.setListaLavorazioni(attivita.getListaLavorazioni().stream().parallel().filter(lavorazione -> lavorazione.getIstanza().getId().equals(idIstanza)).collect(Collectors.toList()));
            }).collect(Collectors.toList());

the following code iterates over a list of objects containing a child that must be filtered and reset on each object of the parent stream.

Also posted a new version of the code using map:

@CachePut(value="sdipp-cache")
public List<Attivita> ottieniListaAttivita(String prefixCall, Long idIstanza) throws DefaultException {
    return servizioGenerico.ottieniCall(prefixCall).getListaAttivita().stream().parallel()
        .map(attivita -> filtroLavorazioniAttivitaSuIstanza(attivita, idIstanza)).collect(Collectors.toList());
}

private Attivita filtroLavorazioniAttivitaSuIstanza(Attivita attivita, Long idIstanza) {
    attivita.setListaLavorazioni(attivita.getListaLavorazioni().stream().parallel()
        .filter(lavorazione -> lavorazione.getIstanza().getId().equals(idIstanza)).collect(Collectors.toList()));
    return attivita;
}

Thanks.


Solution

  • from a design point of view, I would say you can try to encapsulate the filtering logic inside this Attivita class.

    For a simplified example, if you see something like this: pawn.setPosition(pawn.getPosition() + x) This is an anti-pattern known as "feature envy" and it is a sign that this logic belongs inside the Pawn class: pawn.advance(x).

    In your case, maybe you can add something like activita.filterLavorazioniById(idIstanza)

    In regards to performance, how large are these collections? if they are not very large, the overhead of using parallel streams might not be worth it, especially since you are using mutable data.

    As suggested in the comments, it can be a good idea to use immutable objects if you want to safely process these collections in parallel. So, if you'll introduce this filterLavorazioniById() method, it can potentially return a new activita with the filtered data. the final code can look something like this:

    return servizioGenerico.ottieniCall(prefixCall).getListaAttivita()
        .stream().parallel()
        .map(attivita -> attivita.filterLavorazioniById(idIstanza)
        .collect(Collectors.toList());