Удалить объекты из ArrayList на основе заданного критерия
Я хотел бы удалить элемент из ArrayList
в Java, если он соответствует определенному критерию.
т
for (Pulse p : pulseArray) {
if (p.getCurrent() == null) {
pulseArray.remove(p);
}
}
Я могу понять, почему это не работает, но что это хороший способ сделать это?
Ответы
Ответ 1
Вы должны использовать Iterator
для итерации и remove
функции итератора (не списка):
Iterator<Pulse> iter = pulseArray.iterator();
while (iter.hasNext()) {
Pulse p = iter.next();
if (p.getCurrent()==null) iter.remove();
}
Обратите внимание, что функция Iterator # remove называется опциональной, но она реализуется итератором ArrayList.
Здесь код этой конкретной функции из ArrayList.java:
765 public void remove() {
766 if (lastRet < 0)
767 throw new IllegalStateException();
768 checkForComodification();
769
770 try {
771 ArrayList.this.remove(lastRet);
772 cursor = lastRet;
773 lastRet = -1;
774 expectedModCount = modCount;
775 } catch (IndexOutOfBoundsException ex) {
776 throw new ConcurrentModificationException();
777 }
778 }
779
780 final void checkForComodification() {
781 if (modCount != expectedModCount)
782 throw new ConcurrentModificationException();
783 }
784 }
Строка expectedModCount = modCount;
- это то, почему она не будет генерировать исключение, когда вы используете его во время итерации.
Ответ 2
Вы можете использовать Collection :: removeIf (фильтр предикатов) (доступно с Java8 и далее), вот простой пример:
final Collection<Integer> list = new ArrayList<>(Arrays.asList(1, 2));
list.removeIf(value -> value < 2);
System.out.println(list); // outputs "[2]"
Ответ 3
Не нужно использовать итератор. С Java 8 (возможность потоковой передачи и фильтрации и лямбда) вы можете выполнить ее, используя одну строку.
Напр. требуемый код, который выполняет указанную вами операцию, будет:
pulseArray = pulseArray.stream().filter(pulse -> pulse != null).collect(Collectors.toList());
Ответ 4
Когда вы удаляете элемент из того же списка, индекс становится нарушенным. Попробуйте немного по-другому, как показано ниже:
for (int i=0; i < pulseArray.size(); i++) {
Pulse p = (Pulse)pulseArray.get(i);
if (p.getCurrent() == null) {
pulseArray.remove(p);
i--;//decrease the counter by one
}
}
Ответ 5
В качестве альтернативы использованию итератора вы можете использовать библиотеку коллекций Guava. Это имеет то преимущество, что более functional (если вы в этом заняты):
Predicate<Pulse> hasCurrent = new Predicate<Pulse>() {
@Override public boolean apply(Pulse input) {
return (input.getCurrent() != null);
}
};
pulseArray = Lists.newArrayList(Collections2.filter(pulseArray, hasCurrent));
Ответ 6
Вы не можете изменить коллекцию, которую вы повторяете, используя методы в коллекции. Однако некоторые итераторы (включая итераторы на ArrayList
s) поддерживают метод remove()
, который позволяет вам удалять методы в том порядке, в котором вы выполняете итерацию.
Iterator<Pulse> iterator = pulseArray.iterator();
while (iterator.hasNext()) {
Pulse p = iterator.next();
if (p.getCurrent() == null) {
iterator.remove();
}
}
Ответ 7
Использование Iterator даст вам возможность изменять список при повторении через arraylist