Java 8 lambda получить и удалить элемент из списка
Учитывая список элементов, я хочу, чтобы элемент с заданным свойством и удалял его из списка. Лучшее решение, которое я нашел, это:
ProducerDTO p = producersProcedureActive
.stream()
.filter(producer -> producer.getPod().equals(pod))
.findFirst()
.get();
producersProcedureActive.remove(p);
Можно ли комбинировать get и remove в выражении лямбда?
Ответы
Ответ 1
Удалить элемент из списка
objectA.removeIf(x -> conditions);
например:
objectA.removeIf(x -> blockedWorkerIds.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);
ВЫВОД:
В
С
Ответ 2
Хотя поток довольно старый, все же считается, что он предлагает решение с использованием Java8
.
Используйте функцию removeIf
. Временная сложность O(n)
producersProcedureActive.removeIf(producer -> producer.getPod().equals(pod));
Справочник по API: removeIf docs
Предположение: producersProcedureActive
является List
ПРИМЕЧАНИЕ. При таком подходе вы не сможете удержать удаленный элемент.
Ответ 3
Рассмотрите возможность использования итераторов java-ванили для выполнения задачи:
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;
}
<сильные > Преимущества:
- Это ясно и очевидно.
- Он перемещается только один раз и только до соответствующего элемента.
- Вы можете сделать это на любой
Iterable
даже без поддержки stream()
(по крайней мере, тех, кто реализует remove()
на своем итераторе).
Недостатки
- Вы не можете сделать это как единое выражение (требуется вспомогательный метод или переменная)
Что касается
Можно ли комбинировать get и remove в выражении лямбда?
другие ответы ясно показывают, что это возможно, но вы должны знать
- Поиск и удаление могут проходить дважды по списку
-
ConcurrentModificationException
может быть сброшен при удалении элемента из повторяющегося списка
Ответ 4
Прямым решением было бы вызвать ifPresent(consumer)
для Optional, возвращаемого findFirst()
. Этот потребитель будет вызываться, когда необязательный параметр не пуст. Преимущество также состоит в том, что оно не будет выдавать исключение, если операция поиска вернула пустой необязательный параметр, как это сделал бы ваш текущий код; вместо этого ничего не произойдет.
Если вы хотите вернуть удаленное значение, вы можете map
Optional
с результатом вызова 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)
работает непосредственно с индексом.
Ответ 5
Use может использовать фильтр Java 8 и создать другой список, если вы не хотите изменять старый список:
List<ProducerDTO> result = producersProcedureActive
.stream()
.filter(producer -> producer.getPod().equals(pod))
.collect(Collectors.toList());
Ответ 6
Я уверен, что это будет непопулярный ответ, но он работает...
ProducerDTO[] p = new ProducerDTO[1];
producersProcedureActive
.stream()
.filter(producer -> producer.getPod().equals(pod))
.findFirst()
.ifPresent(producer -> {producersProcedureActive.remove(producer); p[0] = producer;}
p[0]
будет либо удерживать найденный элемент, либо быть нулевым.
"Трюк" здесь обходит "эффективно финальную" проблему, используя ссылку на массив, которая является фактически окончательной, но устанавливает ее первый элемент.
Ответ 7
С 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
Ответ 8
Как и другие, это может быть прецедентом для циклов и итераций. На мой взгляд, это самый простой подход. Если вы хотите изменить список на месте, он не может считаться "реальным" функциональным программированием в любом случае. Но вы можете использовать Collectors.partitioningBy()
, чтобы получить новый список с элементами, которые удовлетворяют вашему условию, и новый список тех, которые этого не делают. Конечно, при таком подходе, если у вас есть несколько элементов, удовлетворяющих условию, все они будут в этом списке, а не только в первом.
Ответ 9
Приведенная ниже логика является решением без изменения исходного списка
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"]
Ответ 10
Объединив мою первоначальную идею и ваши ответы, я достиг того, что кажется решением
на мой вопрос:
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;
});
}
Ответ 11
Когда мы хотим получить несколько элементов из списка в новый список (отфильтровать с помощью предиката) и удалить их из существующего списка, я не мог найти правильный ответ нигде.
Вот как мы можем сделать это, используя Java Streaming API.
Map<Boolean, List<ProducerDTO>> classifiedElements = producersProcedureActive
.stream()
.collect(Collectors.partitioningBy(producer -> producer.getPod().equals(pod)));
// get two new lists
List<ProducerDTO> matching = classifiedElements.get(true);
List<ProducerDTO> nonMatching = classifiedElements.get(false);
// OR get non-matching elements to the existing list
producersProcedureActive = classifiedElements.get(false);
Таким образом, вы эффективно удаляете отфильтрованные элементы из исходного списка и добавляете их в новый список.
См. 5.2. Collectors.partitioning В разделе этой статьи.
Ответ 12
Задача: получить и удалить элемент из списка
p.stream().collect( Collectors.collectingAndThen( Collector.of(
ArrayDeque::new,
(a, producer) -> {
if( producer.getPod().equals( pod ) )
a.addLast( producer );
},
(a1, a2) -> {
return( a1 );
},
rslt -> rslt.pollFirst()
),
(e) -> {
if( e != null )
p.remove( e ); // remove
return( e ); // get
} ) );