Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

java 8 nested streams

Suppose you have structure classes like this:

public class Review{
    private Integer idReview;
    private String description;
    private ArrayList<RelReviewImage> images;
}

public class RelReviewImage{
    private Integer idRelReviewImage;
    private Integer idImage;
    private String name;
}

With Java 8 and Streams we want to do a filter for idImage and return Review objects.
Is it possible? One level is easy, but 2 levels we can't find any example or documentation.

like image 344
TrailRunningReview Avatar asked Dec 01 '14 18:12

TrailRunningReview


2 Answers

Guess what you need: (Assume getters are available for Review and RelReviewImage)

List<Review> originalReviews = ...

List<Review> result = originalReviews.stream()
    .filter(review -> review.getImages().stream() //Nested streams. Assume getImages() never null, but empty 
                          .anyMatch(image -> image.getIdImage() == 123)) //'2 level' here
    .collect(Collectors.toList());
like image 168
卢声远 Shengyuan Lu Avatar answered Oct 12 '22 01:10

卢声远 Shengyuan Lu


I think you can get the most maintainable and elegant code here by not trying for a one-liner. :)

When I have these nested structures, I usually create a new method for each level. So that when I'm coding, I only have to have one level in my head at a time.

Try pulling the part that checks if there exists an image with imageId into a Predicate.

A Predicate here is a Function that takes your Review and returns a Boolean that can be filtered on.

public List<Review> filterReviews(){
    Integer idImage = 1;
    List<Review> reviews = new ArrayList<>();
    ...
    List<Review> result = reviews.stream()
            .filter(hasImage(idImage))
            .collect(Collectors.toList());

    return result;
}

private Predicate<Review> hasImage(final Integer idImage){
    return review -> review.images.stream()
            .anyMatch(image -> Objects.equals(image.idImage, idImage));
}

Protip

If the filterReviews-method had taken the Predicate as a parameter, you can use the same method, to filter on all different fields inside Review, by passing different Predicates.

like image 43
tomaj Avatar answered Oct 12 '22 00:10

tomaj