it-swarm.com.ru

Java 8 лямбда-получить и удалить элемент из списка

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

ProducerDTO p = producersProcedureActive
                .stream()
                .filter(producer -> producer.getPod().equals(pod))
                .findFirst()
                .get();
producersProcedureActive.remove(p);

Можно ли объединить get и remove в лямбда-выражении?

52
Marco Stramezzi

Удалить элемент из списка  

objectA.removeIf (x -> условия);

например: objectA.removeIf (x -> заблокированныйWorkerIds.contains (x));

List<String> str1 = new ArrayList<String>();
str1.add("A");
str1.add("B");
str1.add("C");
str1.add("D");

List<String> str2 = new ArrayList<String>();
str2.add("D");
str2.add("E");

str1.removeIf(x -> str2.contains(x)); 

str1.forEach(System.out::println);

Результат: A B C

74
uma shankar

Хотя поток довольно старый, все же считается, что он предлагает решение - с помощью Java8.

Используйте функцию removeIf. Сложность времени O(n)

producersProcedureActive.removeIf(producer -> producer.getPod().equals(pod));

Справочник по API: removeIf docs

Предположение: producersProcedureActive является List

ПРИМЕЧАНИЕ. При таком подходе вы не сможете удержать удаленный элемент.

17
asifsid88

Рассмотрите возможность использования итераторов Java Vanilla для выполнения этой задачи:

public static <T> T findAndRemoveFirst(Iterable<? extends T> collection, Predicate<? super T> test) {
    T value = null;
    for (Iterator<? extends T> it = collection.iterator(); it.hasNext();)
        if (test.test(value = it.next())) {
            it.remove();
            return value;
        }
    return null;
}

Преимущества :

  1. Это ясно и очевидно.
  2. Он проходит только один раз и только до соответствующего элемента.
  3. Вы можете сделать это на любой Iterable, даже без поддержки stream()(по крайней мере, для тех, кто реализует remove() на своем итераторе).

Недостатки :

  1. Вы не можете сделать это на месте как одно выражение (требуется вспомогательный метод или переменная)

Для

Можно ли объединить get и remove в лямбда-выражении?

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

  1. Поиск и удаление могут пройти через список дважды
  2. ConcurrentModificationException может быть выброшено при удалении элемента из списка, который повторяется
15
Vasily Liaskovsky

Прямым решением было бы вызвать ifPresent(consumer) на Optional, возвращаемое findFirst() . Этот потребитель будет вызываться, когда необязательный параметр не пуст. Преимущество также состоит в том, что оно не будет выдавать исключение, если операция поиска вернула пустой необязательный параметр, как это сделал бы ваш текущий код; вместо этого ничего не случится.

Если вы хотите вернуть удаленное значение, вы можете mapOptional в результате вызова remove:

producersProcedureActive.stream()
                        .filter(producer -> producer.getPod().equals(pod))
                        .findFirst()
                        .map(p -> {
                            producersProcedureActive.remove(p);
                            return p;
                        });

Но обратите внимание, что операция remove(Object) будет снова проходить по списку, чтобы найти элемент для удаления. Если у вас есть список с произвольным доступом, например, ArrayList, было бы лучше создать поток поверх индексов списка и найти первый индекс, соответствующий предикату:

IntStream.range(0, producersProcedureActive.size())
         .filter(i -> producersProcedureActive.get(i).getPod().equals(pod))
         .boxed()
         .findFirst()
         .map(i -> producersProcedureActive.remove((int) i));

При таком решении операция remove(int) работает непосредственно с индексом.

9
Tunaki

Use может использовать фильтр Java 8 и создать другой список, если вы не хотите изменять старый список:

List<ProducerDTO> result = producersProcedureActive
                            .stream()
                            .filter(producer -> producer.getPod().equals(pod))
                            .collect(Collectors.toList());
5
Toi Nguyen

Я уверен, что это будет непопулярный ответ, но это работает ...

ProducerDTO[] p = new ProducerDTO[1];
producersProcedureActive
            .stream()
            .filter(producer -> producer.getPod().equals(pod))
            .findFirst()
            .ifPresent(producer -> {producersProcedureActive.remove(producer); p[0] = producer;}

p[0] будет содержать найденный элемент или будет нулевым.

«Уловка» здесь заключается в том, чтобы обойти «эффективно окончательную» проблему с помощью ссылки array , которая является окончательной, но с установкой ее первого элемента.

3
Bohemian

С Eclipse Collections вы можете использовать detectIndex вместе с remove(int) в любом Java.util.List.

List<Integer> integers = Lists.mutable.with(1, 2, 3, 4, 5);
int index = Iterate.detectIndex(integers, i -> i > 2);
if (index > -1) {
    integers.remove(index);
}

Assert.assertEquals(Lists.mutable.with(1, 2, 4, 5), integers);

Если вы используете тип MutableList из Eclipse Collections, вы можете вызвать метод detectIndex прямо из списка.

MutableList<Integer> integers = Lists.mutable.with(1, 2, 3, 4, 5);
int index = integers.detectIndex(i -> i > 2);
if (index > -1) {
    integers.remove(index);
}

Assert.assertEquals(Lists.mutable.with(1, 2, 4, 5), integers);

Примечание: я являюсь коммиттером для коллекций Eclipse

2
Donald Raab

Как и предполагали другие, это может быть случай использования циклов и итераций. На мой взгляд, это самый простой подход. Если вы хотите изменить список на месте, его все равно нельзя считать «настоящим» функциональным программированием. Но вы можете использовать Collectors.partitioningBy(), чтобы получить новый список с элементами, которые удовлетворяют вашему условию, и новый список тех, которые не соответствуют. Конечно, при таком подходе, если у вас есть несколько элементов, удовлетворяющих условию, все они будут в этом списке, а не только в первом.

1
user140547

Приведенная ниже логика является решением без изменения исходного списка

List<String> str1 = new ArrayList<String>();
str1.add("A");
str1.add("B");
str1.add("C");
str1.add("D");

List<String> str2 = new ArrayList<String>();
str2.add("D");
str2.add("E");

List<String> str3 = str1.stream()
                        .filter(item -> !str2.contains(item))
                        .collect(Collectors.toList());

str1 // ["A", "B", "C", "D"]
str2 // ["D", "E"]
str3 // ["A", "B", "C"]
0
KimchiMan

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

public ProducerDTO findAndRemove(String pod) {
    ProducerDTO p = null;
    try {
        p = IntStream.range(0, producersProcedureActive.size())
             .filter(i -> producersProcedureActive.get(i).getPod().equals(pod))
             .boxed()
             .findFirst()
             .map(i -> producersProcedureActive.remove((int)i))
             .get();
        logger.debug(p);
    } catch (NoSuchElementException e) {
        logger.error("No producer found with POD [" + pod + "]");
    }
    return p;
}

Он позволяет удалить объект, используя функцию remove(int), которая больше не пересекает список (Как предложено @Tunaki) и позволяет вернуть удаленный объект в Вызывающую функцию.

Я читаю ваши ответы, в которых предлагается выбрать безопасные методы, такие как ifPresent вместо get, но я не нахожу способ использовать их в этом сценарии.

Есть ли важный недостаток в этом виде решения? 

Редактировать следующий совет @Holger

Это должна быть та функция, которая мне нужна

public ProducerDTO findAndRemove(String pod) {
    return IntStream.range(0, producersProcedureActive.size())
            .filter(i -> producersProcedureActive.get(i).getPod().equals(pod))      
            .boxed()                                                                
            .findFirst()
            .map(i -> producersProcedureActive.remove((int)i))
            .orElseGet(() -> {
                logger.error("No producer found with POD [" + pod + "]"); 
                return null; 
            });
}
0
Marco Stramezzi