Как с помощью одного стрима выполнить 2 условия, первое из которых требует проверки по всему стриму?

У меня есть коллекция чисел, и, если она содержит числа кратные 3, я вывожу на экран только их, а если нет - то числа, кратные пяти. Я могу сделать это так, но есть ли способ сделать это действие внутри одного стрима?

if (numbers.stream()
               .filter(el -> el % 3 == 0)
               .count() > 0) {
        numbers = numbers.stream().
                         filter(el -> el % 3 == 0)
                         .collect(Collectors.toList());
    } else {
        numbers = numbers.stream()
                         .filter(el -> el % 5 == 0)
                         .collect(Collectors.toList());
    }

Ответы (5 шт):

Автор решения: Elizaveta

Может что-то в этом духе (могла попутать функции но смысл остается)

Function<List<Integer>, Predicate<Integer>> func = list - > {
    return list.stream().anyMatch(el -> el % 3 == 0) ? 
        el - > el % 3 == 0 : el - > el % 5 == 0;
} 
numbers.stream()
    .filter(number - > func.apply(numbers).test(number))
    .forEach(System.out::println);
→ Ссылка
Автор решения: Tom

Решил так:

numbers = numbers.stream()
                 .filter(numbers.stream()
                 .anyMatch(el -> el % 3 == 0)
                      ? el -> el % 3 == 0 : el -> el % 5 == 0)
                 .collect(Collectors.toList());
→ Ссылка
Автор решения: Elizaveta

Типо так (вариант #2)

Function<List<Integer>, Predicate<Integer>> func = list - > {
    return list.stream().anyMatch(el -> el % 3 == 0) ? 
        el - > el % 3 == 0 : el - > el % 5 == 0;
} 
Predicate<Integer> predicate = func.apply(numbers);
numbers.stream()
    .filter(predicate::test)
    .forEach(System.out::println); 
→ Ссылка
Автор решения: Alex Rudenko

Более корректное решение внутри одного стрима: накопить подходящие кратные числа в разных списках, и если список кратных тройке непустой, выводить его, иначе выводить список кратных пятерке:

List<Integer> filtered = numbers.stream()
    .filter(x -> x % 3 == 0 || x % 5 == 0)
    .collect(Collectors.collectingAndThen(
        Collectors.partitioningBy(x -> x % 3 == 0),
        (map) ->  map.get(true).isEmpty() ? map.get(false) : map.get(true)
    ));

System.out.println(filtered);

Другие Предыдущие предложенные решения использовали два прохождения по стриму в худшем случае.

→ Ссылка
Автор решения: tym32167

Также решение в один проход. Суть его в групировке значений по делимости на 3 и 5 в сортированную хеш таблицу.

numbers.stream().filter(x -> x % 3 == 0 || x % 5 == 0)
        .collect(Collectors.groupingBy(x -> x % 3 == 0 ? 1 : 2, 
            () -> new TreeMap<>(), Collectors.toList()))
        .firstEntry()
        .getValue()
        .forEach(System.out::println);

На случай, если в исходных данных нет значений, которые бы делились на 3 или 5, то можно заранее предусмотреть пустой вывод. Код наверняка можно изящней переписать, но как умею )

TreeMap<Integer, List<Integer>> map = new TreeMap<>();
map.put(Integer.MAX_VALUE, new ArrayList<>());

numbers.stream().filter(x -> x % 3 == 0 || x % 5 == 0)
        .collect(Collectors.groupingBy(x -> x % 3 == 0 ? 1 : 2, 
            () -> map, Collectors.toList()))
        .firstEntry()
        .getValue()
        .forEach(System.out::println);
→ Ссылка