使用Java 8 Streams、map、filter和reduce查找某个演员的电影作品

5
我正在尝试使用Java 8 Stream API进行实验,并希望使用Java 8 stream filter map reduce转换以下方法。
我有一个电影列表,每个电影对象都有一个演员列表以及其他字段。
我想找到所有由特定名字和姓氏的演员参与的电影。
下面的方法基于Java 7,其中我循环遍历电影列表,然后循环遍历该电影的演员列表。如果找到具有该名字和姓氏的演员,则中断内部循环并将该电影添加到返回的电影列表中。
注释代码可以正常工作,我可以获得正确的电影列表。
我的问题是如何使用Java 8流重写此代码。我可以看到这是一种映射、过滤、归约的问题,但我无法想出清晰的解决方案。
public List<Movie> getMoviesForActor(String firstName, String lastName) {

    final List<Movie> allMovies = movieRepository.getAllMovies();
    final Predicate<Actor> firstNamePredicate = actor -> actor.getFirstName().equalsIgnoreCase(firstName);
    final Predicate<Actor> lastNamePredicate = actor -> actor.getLastName().equalsIgnoreCase(lastName);

    final List<Movie> movies = new ArrayList<>();
    //        for (Movie movie : allMovies) {
    //            boolean actorFound = false;
    //            for (Actor actor : movie.getActors()) {
    //                if(firstName.equalsIgnoreCase(actor.getFirstName()) && lastName.equalsIgnoreCase(actor.getLastName())) {
    //                    actorFound = true;
    //                    break;
    //                }
    //            }
    //            if(actorFound) {
    //                movies.add(movie);
    //            }
    //        }

    final List<Actor> actors = allMovies.stream()
            .flatMap(
                    movie -> movie.getActors().stream().filter(firstNamePredicate.and(lastNamePredicate))
            ).collect(Collectors.toList());
    return movies;
}

如果我在电影上进行流式处理并将其平铺,然后在其中流式处理演员列表,那么如何再次获取仅包含此名字和姓氏的演员列表的电影列表?

4个回答

3

既然其他答案已经讲解了如何在java-8中解决这个问题,那么本解决方案将介绍在java-9中引入的全新Collectors.filtering方法。因此,仅供日后参考。

List<Movie> movies = allMovies.stream()
                .collect(Collectors.filtering(
                      m -> m.getActors().stream().anyMatch(firstNamePredicate.and(lastNamePredicate)),
                Collectors.toList()));

2
有趣的替代方案。只需记住,“filtering()”收集器在多级归约中最有用,例如在“groupingBy”或“partitioningBy”之后使用。 - Naman

2
在遍历可迭代对象并在找到第一个匹配元素后停止的情况下,可以很容易地使用Java8中的“anyMatch”短路终止操作来实现。然后将“anyMatch”的结果传递给“filter”运算符,以获取所有符合给定条件的电影。
我建议您使用内联谓词,而不是单独定义它们(除非您在其他地方重用它们)。这会导致更紧凑的代码,不那么冗长。以下是它的外观。
movies.stream()
    .filter(m -> m.getActors().stream()
        .anyMatch(
            a -> a.getFirstName().equalsIgnoreCase(firstName) 
                && a.getLastName().equalsIgnoreCase(lastName)))
    .collect(Collectors.toList());

如果出于某些原因,您确实需要使用问题声明中给出的预定义谓词,则可以按以下方式执行:

Original Answer翻译成:“最初的回答”

movies.stream()
    .filter(m -> m.getActors().stream()
        .anyMatch(firstNamePredicate.and(lastNamePredicate)))
    .collect(Collectors.toList());

2

用现有代码的更好方式(函数式)编写它的方法是:

最初的回答:

final Predicate<Movie> movieIncludesActor = movie -> movie.getActors()
        .stream()
        .anyMatch(firstNamePredicate.and(lastNamePredicate)); // check both the condition for all actors
final List<Movie> movies = allMovies.stream()
        .filter(movieIncludesActor) // movie which has such an actor
        .collect(toList());

1

还有一种解决方案。

有时使用错误的集合类型会让生活变得困难。我建议Movie.getActors()返回一个Set<Actor>而不是List<Actor>。这将使处理更加容易。

private class Movie {
    public Set<Actor> getActors() {
        return null;
    }
}

private class Actor {
    private final String firstName;
    private final String lastName;

    private Actor(String firstName, String lastName) {
        this.firstName = firstName;
        this.lastName = lastName;
    }

    public String getFirstName() {
        return firstName;
    }

    public String getLastName() {
        return lastName;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof Actor)) return false;
        Actor actor = (Actor) o;
        return firstName.equals(actor.firstName) &&
                lastName.equals(actor.lastName);
    }

    @Override
    public int hashCode() {
        return Objects.hash(firstName, lastName);
    }
}

final List<Movie> allMovies = Collections.EMPTY_LIST;

public List<Movie> getMoviesForActor(String firstName, String lastName) {
    Actor actor = new Actor(firstName, lastName);

    return allMovies.stream()
            .filter(m -> m.getActors().contains(actor))
            .collect(Collectors.toList());
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接