Фильтр по вложенному списку с использованием Java 8 Stream API - PullRequest
4 голосов
/ 22 апреля 2019

Я не могу преобразовать приведенный ниже фрагмент в потоковый формат Java 8.

List<String> titles = Arrays.asList("First Name", "Last Name");

for (FirstClass first : firstClassList) {
    for (SecondClass second : first.getSecondClassList()) {
        for (ThirdClass third : second.getThirdClassList()) {                   

            if(!titles.contains(third.getField())) {
                second.getThirdClassList().remove(third);
            }

        }
    }
}  

Я сравниваю объект вложенного списка третьего уровня со списком входных полей. Если поля не совпадают, я удаляю их из исходного списка. Как я могу добиться этого, используя синтаксис Java 8.

Редактировать: я хочу, чтобы список FirstClass был возвращен.

Ответы [ 4 ]

3 голосов
/ 22 апреля 2019

Я не думаю, что потоки выигрывают вам что-либо в этом случае.Все, что вы делаете, это перебираете вложенные списки, и либо расширенный цикл for, либо forEach более прост.

Улучшения могут быть получены при использовании removeIfизменить список и, возможно, вывести логику отклонения из цикла:

Predicate<ThirdClass> reject = third -> !titles.contains(third.getField());

firstClassList.forEeach(first ->
    first.getSecondClassList().forEach(second ->
        second.getThirdClassList().removeIf(reject)
    )
);
1 голос
/ 22 апреля 2019

Получите поток объектов SecondClass, сгладив firstClassList, и для каждого SecondClass получите отфильтрованный список ThirdClass объектов и установите его обратно в SecondClass

List<String> titles = Arrays.asList("First Name", "Last Name");

firstClassList
    .stream()
    .flatMap(firstClass -> firstClass.getSecondClassList().stream())
    .forEach(secondClass -> {
             List<ThirdClass> filteredThirdClasses = secondClass.getThirdClassList()
                        .stream()
                        .filter(thirdClass -> titles.contains(thirdClass.getField()))
                        .collect(toList());

             secondClass.setThirdClassList(filteredThirdClasses);
         }
    );
1 голос
/ 22 апреля 2019

Сначала вы можете использовать Stream.map() и Stream.flatMap(), чтобы получить Stream, содержащий List из ThirdClass. Чтобы удалить элементы, соответствующие условию, вы можете использовать Collection.removeIf(), который удаляет все элементы из коллекции, соответствующие данному условию:

firstClassList.stream()                         // Stream<FirstClass>
        .map(FirstClass::getSecondClassList)    // Stream<List<SecondClass>>
        .flatMap(Collection::stream)            // Stream<SecondClass>
        .map(SecondClass::getThirdClassList)    // Stream<List<ThirdClass>>
        .forEach(thirdList -> thirdList.removeIf(third -> !titles.contains(third.getField())));

Это изменяет исходный список, как вы это делали в своем примере. Затем вы можете использовать firstClassList как результат для дальнейшей обработки.

Кроме того, я бы порекомендовал использовать Set<String> вместо List<String> для ваших названий, поскольку его временная сложность составляет O (1) вместо О (п) * 1 026 *:

Set<String> titles = new HashSet<>(Arrays.asList("First Name", "Last Name"));
0 голосов
/ 22 апреля 2019

Если предположить, что три уровня в вашей структуре данных являются коллекциями, решение может быть следующим:

  1. сплющить структуру до уровня потока листьев
  2. фильтр по требуемым заголовкам
final List<ThirdClassList> results = firstClassList
    .stream()
    .flatMap(FirstClassList::getSecondClassList)
    .flatMap(FirstClassList::getThirdClassList)
    .filter(third -> !titles.contains(third))
    .collect(toList());

Это даст вам объекты уровня листьев, которые нужно удалить, хотя это, конечно, только половина решения, вы все равно хотите удалить их.

Если вы являетесь автором классов списков, то, возможно, у вас может быть ссылка с каждого третьего уровня на его «родительский» второй уровень, поэтому удаление - это относительно простой второй шаг:

results.forEach(third -> third.parent().remove(this));

где third.parent() возвращает объект второго уровня.

...