Найдите фильмы, в которых актер с именем и фамилией работал, используя потоки Java 8, карту, фильтр, уменьшение - PullRequest
0 голосов
/ 08 февраля 2019

Я пытаюсь обойти с помощью Java 8 Stream API и хотел преобразовать следующий метод, используя уменьшение фильтра потока Java 8.

У меня есть список фильмов, и у каждого объекта Movie есть список актеров.с другими полями.

Я хочу найти все фильмы, в которых работал актер с определенным именем и фамилией.

Метод, представленный ниже, основан на 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 ]

0 голосов
/ 08 февраля 2019

И еще одно решение.

Иногда использование неправильного типа коллекции усложняет вам жизнь.Я бы предложил 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());
}
0 голосов
/ 08 февраля 2019

Просто лучший (функциональный) способ написания его с использованием существующего кода:

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());
0 голосов
/ 08 февраля 2019

Так как другие ответы уже касались способов решения проблемы в java-8, с этим решением вы можете использовать все новые Collectors.filtering, представленные в java-9. Так что просто оставив это здесьдля дальнейшего использования.

List<Movie> movies = allMovies.stream()
                .collect(Collectors.filtering(
                      m -> m.getActors().stream().anyMatch(firstNamePredicate.and(lastNamePredicate)),
                Collectors.toList()));
0 голосов
/ 08 февраля 2019

Нахождение первого подходящего элемента при циклическом повторении итерации и разрыве после его обнаружения может быть легко достигнуто с помощью операции терминала с коротким замыканием anyMatch в Java8.Затем передайте результат anyMatch оператору filter, чтобы получить все фильмы, соответствующие заданным критериям.

Я бы предпочел использовать встроенные предикаты вместо того, чтобы определять их отдельно, если только вы не используете их где-то еще.Это приводит к более сжатому коду, который является менее подробным.Вот как это выглядит.

movies.stream()
    .filter(m -> m.getActors().stream()
        .anyMatch(
            a -> a.getFirstName().equalsIgnoreCase(firstName) 
                && a.getLastName().equalsIgnoreCase(lastName)))
    .collect(Collectors.toList());

По какой-то причине, если вам действительно нужно использовать предопределенные предикаты, как указано в описании проблемы, вы можете сделать это следующим образом:

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