Как эффективно отфильтровать List с помощью Stream API?
Дана задача:
Метод должен вернуть список, который содержит симметричную разность двух подмножеств целочисленных значений:
- первое подмножество: все чётные значения в списке
integerList;- второе подмножество: значения в списке
integerListза исключением первых k элементов.- Результат должен быть отсортирован по убыванию.
Параметры:
k положительное целое число; integerList список положительных целых чисел.
Входные данные:
k = 5
integerList = [1, 2, 3, 4, 5, 6, 7, 8, 9]
Вывод: [9, 7, 4, 2]
Я получил 2 коллекции:
- С
evenчислами от начальной коллекции. - После метода
skipна основной коллекции.
У меня проблема заключается в том, как сравнить их с основной коллекцией (может flatMap) - это основное, что я не могу понять.
И 1 и 2 пункты я делал отдельными шагами, как можно сделать так, чтобы все было только в 1 потоке?
Мое начало..
public static List<Integer> f(int k, List<Integer> integerList) {
List<Integer> evens = integerList.stream().filter(even -> even % 2 == 0).collect(Collectors.toList());
List<Integer> skippedList = integerList.stream().skip(k).collect(Collectors.toList());
List<Integer> sum = new ArrayList<>(evens);
sum.addAll(skippedList);
System.out.println(sum);
return null;
}
Ответы (2 шт):
Скорее всего, можно улучшить, но так работает:
import java.util.*;
public class Main {
public static List<Integer> f(int k, List<Integer> integerList) {
List<Integer> evens = integerList.stream().filter(even -> even % 2 == 0).collect(Collectors.toList());
List<Integer> skippedList = integerList.stream().skip(k).collect(Collectors.toList());
return Stream.concat(evens.stream().filter(e -> !skippedList.contains(e)),
skippedList.stream().filter(e -> !evens.contains(e))).
sorted(Comparator.reverseOrder()).collect(Collectors.toList());
}
public static void main(String[] args) {
System.out.println(f(5, Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9)));
}
}
[9, 7, 4, 2]
Достаточно правильно отфильтровать данные, чтобы сразу получить нужный результат, и затем отсортировать полученный список.
Поскольку заданы такие условия для отбора:
- любые чётные числа
- числа после индекса
k - числа, отвечающие обоим условиям, должны быть исключены (симметричная разность двух списков)
В результате должны остаться только те данные, для которых одновременно удовлетворяется лишь одно из условий, то есть:
- любые чётные числа до индекса
kИЛИ - любые нечётные числа после индекса
k
Таким образом, достаточно переписать метод следующим образом:
public static List<Integer> f(int k, List<Integer> list) {
return IntStream.range(0, list.size())
.filter(i -> i < k && list.get(i) % 2 == 0 || i >= k && list.get(i) % 2 == 1)
.mapToObj(list::get)
.sorted(Comparator.reverseOrder())
.collect(Collectors.toList());
}
Тест:
System.out.println(f(5, Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9)));
Результат:
[9, 7, 4, 2]
Условие фильтра можно ещё упростить, если воспользоваться операцией "исключающего ИЛИ": числа с индексом, меньшим k XOR НЕчётные числа, или ещё один эквивалент: числа с индексом НЕ меньшим k XOR чётные числа:
public static List<Integer> f(int k, List<Integer> list) {
return IntStream.range(0, list.size())
.filter(i -> i < k ^ list.get(i) % 2 != 0)
//.filter(i -> i >= k ^ list.get(i) % 2 == 0)
.mapToObj(list::get)
.sorted(Comparator.reverseOrder())
.collect(Collectors.toList());
}
Для проверки на нечётность следует использовать x % 2 != 0, так как для отрицательных чисел остаток от деления на 2 отрицателен и равен -1.