Альтернатива для фильтрации только первого элемента списка, который соответствует некоторому элементу - PullRequest
0 голосов
/ 12 мая 2018

Я пытаюсь придумать альтернативу методу List для remove(int index) и remove(T element). Где я беру список и делаю некоторую фильтрацию и возвращаю новый список без элемента, который требуется удалить. Я хочу сделать это функционально, поскольку я не хочу изменять исходный список.

Вот моя попытка.

List<Integer> integers = Arrays.asList(2, 4, 1, 2, 5, 1);
Integer elementToRemove = 1;
List<Integer> collect = 
           integers.stream()
                   .filter(elements -> !elements.equals(elementToRemove))
                   .collect(Collectors.toList());

Это удалит все 1.

Я не хочу удалять только первый 1, поэтому у меня останется список вроде [2,4,2,5,1]

Я знаю, как это сделать, используя indexOf() и sublist() и addAll(). Но я чувствую, что это не так хорошо, как использование потоков.

Поиск функциональных решений с использованием потоков для реализации remove(int index) и remove(T element).

Ответы [ 3 ]

0 голосов
/ 12 мая 2018

Я согласен с @ Aominè, но это может быть альтернативой потоковому API

IntStream.range(0,integers.size())
           .filter(i->i != integers.indexOf(elementToRemove)) 
           .mapToObj(i->integers.get(i))
           .collect(Collectors.toList());

Как прокомментировал @ Aominè для оптимизации, сначала найдите индекс elementToRemove, а затем используйте его в фильтре.

0 голосов
/ 12 мая 2018

В то время как мой другой ответ определенно является способом, который я рекомендую продолжить, @Hadi также предоставил альтернативу «потока», которая также действительна. Я решил поэкспериментировать с разными способами достижения того же результата, используя функции JDK-8.

В JDK-9 есть методы takeWhile и dropWhile , где первый возвращает поток, состоящий из самого длинного префикса элементов, взятых из потока, которые соответствуют данному предикату.

Последний возвращает поток, состоящий из оставшихся элементов данного потока после удаления самого длинного префикса элементов, соответствующих данному предикату.

Идея состоит в том, чтобы потреблять элементы, в то время как они не равны elementToRemove:

integers.stream()
        .takeWhile(e -> !Objects.equals(e, elementToRemove))

и отбросьте элементы, пока они не равны elementToRemove и skip(1), чтобы исключить elementToRemove:

integers.stream()
        .dropWhile(e -> !Objects.equals(e, elementToRemove))
        .skip(1)

, следовательно, дает два потока, где первый поток - это все предшествующие числа до elementToRemove, а второй поток плюс skip(1) - это все элементы после elementToRemove, тогда мы просто объединяем их и собираем в реализацию списка.

List<Integer> result = Stream.concat(integers.stream()
                        .takeWhile(e -> !Objects.equals(e, elementToRemove)),
                integers.stream()
                        .dropWhile(e -> !Objects.equals(e, elementToRemove))
                        .skip(1))
                .collect(Collectors.toList());

Если элемент для удаления не существует в списке, takeWhile будет использовать все элементы, а dropWhile отбросит все элементы, и когда мы объединим эти два потока, мы вернем исходные элементы.

В целом это даст тот же результат, что и другие ответы.

Однако не используют это решение в рабочем коде , так как это неоптимально и не очевидно для глаз, что делает код. это только здесь, чтобы показать различные способы выполнить указанное требование.

0 голосов
/ 12 мая 2018

Я хочу сделать это функционально, поскольку я не хочу изменять исходный список.

Вы по-прежнему можете выполнять операцию удаления и не изменять источник без перехода в рабочее состояние.

Но я чувствую, что это не так хорошо, как использование потоков.

Скорее, наоборот, лучше без потоков:

List<Integer> result = new ArrayList<>(integers); // copy the integers list
result.remove(Integer.valueOf(elementToRemove)); // remove from the new list leaving the old list unmodified
...