Java 8 вложенных потоков

Предположим, что у вас есть структурные классы:

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;
}

С Java 8 и Stream мы хотим сделать фильтр для idImage и вернуть объекты Review.
Является ли это возможным? Один уровень прост, но 2 уровня мы не можем найти ни одного примера или документации.

Ответ 1

Угадайте, что вам нужно: (Предположите, что геттеры доступны для Review и 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());

Ответ 2

Я думаю, вы можете получить самый удобный и элегантный код здесь, не пытаясь сделать однострочный.:)

Когда у меня есть эти вложенные структуры, я обычно создаю новый метод для каждого уровня. Поэтому, когда я кодирую, мне нужно иметь только один уровень в голове.

Попробуйте потянуть деталь, которая проверяет, существует ли изображение с imageId в Predicate.

A Predicate здесь Function, который принимает ваш Review и возвращает Boolean, который можно отфильтровать.

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

Если метод filterReviews принял параметр Predicate в качестве параметра, вы можете использовать тот же метод для фильтрации по всем различным полям внутри Review, передав разные Predicates.

Ответ 3

Вот несколько основных идей:

public static void main(String[] args) {
    // TODO Auto-generated method stub

    List<Review> reviews = new ArrayList<>();
    for(int i = 0; i < 50; i++) {
        List<RelReviewImage> revImg = new ArrayList<>();
        for(int j = 0; j < 5; j++) {
            revImg.add(new RelReviewImage(j, j, "img_"+j));
        }
        reviews.add(new Review(i, "rev" + i, revImg));
    }

    List<Review> result = reviews.stream().filter(r -> r.getImages().stream().anyMatch(i -> i.getIdImage().intValue() < 2)).collect(Collectors.toList());

}

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

    public Review(Integer id, String desc, List<RelReviewImage> img) {
        super();
        this.idReview = id;
        this.description = desc;
        this.images = img;
    }

    public Integer getIdReview() {
        return idReview;
    }

    public void setIdReview(Integer idReview) {
        this.idReview = idReview;
    }

    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description;
    }

    public List<RelReviewImage> getImages() {
        return images;
    }

    public void setImages(List<RelReviewImage> images) {
        this.images = images;
    }
}

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

    public RelReviewImage(Integer id, Integer id_img, String name) {
        super();
        this.idRelReviewImage = id;
        this.idImage = id_img;
        this.name = name;
    }

    public Integer getIdRelReviewImage() {
        return idRelReviewImage;
    }

    public void setIdRelReviewImage(Integer idRelReviewImage) {
        this.idRelReviewImage = idRelReviewImage;
    }

    public Integer getIdImage() {
        return idImage;
    }

    public void setIdImage(Integer idImage) {
        this.idImage = idImage;
    }

    public String getName() {
        return name;
    }

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