Удалить данные из ArrayList с помощью цикла For-loop
У меня возникла странная проблема.
Я думал, что это будет стоить мне нескольких минут, но сейчас я боюсь несколько часов...
Вот что я получил:
for (int i = 0; i < size; i++){
if (data.get(i).getCaption().contains("_Hardi")){
data.remove(i);
}
}
data
- это ArrayList.
В ArrayList я получил несколько строк (всего 14 или около того), и 9 из них получили имя _Hardi в нем.
И с приведенным выше кодом я хочу удалить их.
Если я replace data.remove(i);
с System.out.println
, то он выдает что-то 9 раз, что хорошо, потому что _Hardi находится в ArrayList 9 раз.
Но когда я использую data.remove(i);
, тогда он не удаляет все 9, а только несколько.
Я сделал несколько тестов, и я также видел следующее:
Когда я переименую строки, чтобы:
Hardi1
hardi2
Hardi3
Hardi4
Hardi5
Hardi6
Затем он удаляет только четные числа (1, 3, 5 и т.д.).
Он все время пропускает 1, но не может понять, почему.
Как это исправить? Или, может быть, другой способ их удалить?
Ответы
Ответ 1
Проблема здесь в том, что вы выполняете итерацию от 0 до размера и внутри цикла вы удаляете элементы. Удаление элементов уменьшит размер списка, который не удастся при попытке получить доступ к индексам, размер которых превышает эффективный размер (размер после удаленных элементов).
Для этого есть два подхода.
Удалите с помощью итератора, если вы не хотите иметь дело с индексом.
for (Iterator<Object> it = data.iterator(); it.hasNext();) {
if (it.next().getCaption().contains("_Hardi")) {
it.remove();
}
}
Else, удалить с конца.
for (int i = size-1; i >= 0; i--){
if (data.get(i).getCaption().contains("_Hardi")){
data.remove(i);
}
}
Ответ 2
Вы не должны удалять элементы из списка, пока вы перебираете его. Вместо этого используйте Iterator.remove()
как:
for (Iterator<Object> it = list.iterator(); it.hasNext();) {
if ( condition is true ) {
it.remove();
}
}
Ответ 3
Каждый раз, когда вы удаляете элемент, вы меняете индекс перед ним (поэтому, когда вы удаляете список [1], список [2] становится списком [1], следовательно, пропускается.
Здесь очень простой способ: (вместо обратного отсчета)
for(int i = list.size() - 1; i>=0; i--)
{
if(condition...)
list.remove(i);
}
Ответ 4
Это имеет смысл, если вы обдумаете это. Скажем, у вас есть список [A, B, C]
. Первый проходит через цикл, i == 0
. Вы видите элемент A
, а затем удалите его, поэтому теперь список [B, C]
, а элемент 0 - B
. Теперь вы увеличиваете i
в конце цикла, так что вы смотрите list[1]
, который равен C
.
Одним из решений является уменьшение i
всякий раз, когда вы удаляете элемент, чтобы он "отменял" последующий приращение. Лучшим решением, как указано выше, является использование Iterator<T>
, который имеет встроенную функцию remove()
.
Говоря в целом, это хорошая идея, когда вы сталкиваетесь с такой проблемой, чтобы вытащить листок бумаги и притвориться, что вы компьютер - пройти каждый шаг цикла, записывая все переменные, поскольку вы идти. Это сделало бы "пропуск" ясным.
Ответ 5
for (Iterator<Object> it = data.iterator(); it.hasNext();) {
if ( it.getCaption().contains("_Hardi")) {
it.remove(); // performance is low O(n)
}
}
Если операция удаления требуется в списке в списке. Его лучше использовать LinkedList, который дает лучшую производительность Big O(1)
(примерно).
Где в производительности ArrayList O(n)
(примерно). Таким образом, удар очень высок при удалении операции.
Ответ 6
Потому что, когда вы удаляете элемент из списка, элементы списка перемещаются вверх. Поэтому, если вы удалите первый элемент, то есть при индексе 0, элемент с индексом 1 будет смещен в индекс 0, но ваш счетчик циклов будет увеличиваться на каждой итерации. поэтому вместо того, чтобы получить обновленный элемент 0-го индекса, вы получаете 1-й элемент индекса. Поэтому просто уменьшайте счетчик каждый раз, когда вы удаляете элемент из своего списка.
Вы можете использовать приведенный ниже код, чтобы он работал нормально:
for (int i = 0; i < data.size(); i++){
if (data.get(i).getCaption().contains("_Hardi")){
data.remove(i);
i--;
}
}
Ответ 7
Уже поздно, но это может сработать для кого-то.
Iterator<YourObject> itr = yourList.iterator();
// remove the objects from list
while (itr.hasNext())
{
YourObject object = itr.next();
if (Your Statement) // id == 0
{
itr.remove();
}
}
Ответ 8
Я не понимаю, почему это решение является лучшим для большинства людей.
for (Iterator<Object> it = data.iterator(); it.hasNext();) {
if (it.next().getCaption().contains("_Hardi")) {
it.remove();
}
}
Третий аргумент пуст, потому что был перенесен в следующую строку. Более того, it.next()
не только увеличивает переменную цикла, но и использует для получения данных. Для меня использование цикла for
вводит в заблуждение. Почему вы не используете while
?
Iterator<Object> it = data.iterator();
while (it.hasNext()) {
Object obj = it.next();
if (obj.getCaption().contains("_Hardi")) {
it.remove();
}
}
Ответ 9
Это происходит потому, что, удаляя элементы, вы изменяете индекс ArrayList
.
Ответ 10
Поскольку ваш индекс больше не подходит, как только вы удаляете значение
Кроме того, вы не сможете перейти на size
, так как если вы удалите один элемент, размер изменится.
Для достижения этой цели вы можете использовать iterator
.
Ответ 11
В дополнение к существующим ответам вы можете использовать обычный цикл while с условным приращением:
int i = 0;
while (i < data.size()) {
if (data.get(i).getCaption().contains("_Hardi"))
data.remove(i);
else i++;
}
Обратите внимание, что data.size()
должен вызываться каждый раз в условии цикла, в противном случае вы получите исключение IndexOutOfBoundsException
, поскольку каждый удаленный элемент изменяет исходный размер вашего списка.
Ответ 12
import java.util.ArrayList;
public class IteratorSample {
public static void main(String[] args) {
// TODO Auto-generated method stub
ArrayList<Integer> al = new ArrayList<Integer>();
al.add(1);
al.add(2);
al.add(3);
al.add(4);
System.out.println("before removal!!");
displayList(al);
for(int i = al.size()-1; i >= 0; i--){
if(al.get(i)==4){
al.remove(i);
}
}
System.out.println("after removal!!");
displayList(al);
}
private static void displayList(ArrayList<Integer> al) {
for(int a:al){
System.out.println(a);
}
}
}
выход:
перед удалением!!
1
2
3
4
после удаления!!
1
2
3
Ответ 13
Существует более простой способ решить эту проблему без создания нового объекта итератора. Вот концепция. Предположим, что ваш массивList содержит список имен:
names = [James, Marshall, Susie, Audrey, Matt, Carl];
Чтобы удалить все из Susie forward, просто получите индекс Susie и назначьте его новой переменной:
int location = names.indexOf(Susie);//index equals 2
Теперь, когда у вас есть индекс, скажите java, чтобы подсчитать количество раз, когда вы хотите удалить значения из списка arrayList:
for (int i = 0; i < 3; i++) { //remove Susie through Carl
names.remove(names.get(location));//remove the value at index 2
}
Каждый раз, когда выполняется значение цикла, arrayList уменьшается по длине. Поскольку вы установили значение индекса и подсчитываете количество раз, чтобы удалить значения, вы все настроены. Ниже приведен пример вывода после каждого прохода через:
[2]
names = [James, Marshall, Susie, Audrey, Matt, Carl];//first pass to get index and i = 0
[2]
names = [James, Marshall, Audrey, Matt, Carl];//after first pass arrayList decreased and Audrey is now at index 2 and i = 1
[2]
names = [James, Marshall, Matt, Carl];//Matt is now at index 2 and i = 2
[2]
names = [James, Marshall, Carl];//Carl is now at index 3 and i = 3
names = [James, Marshall,]; //for loop ends
Вот фрагмент того, каким может выглядеть ваш последний метод:
public void remove_user(String name) {
int location = names.indexOf(name); //assign the int value of name to location
if (names.remove(name)==true) {
for (int i = 0; i < 7; i++) {
names.remove(names.get(location));
}//end if
print(name + " is no longer in the Group.");
}//end method
Ответ 14
Это обычная проблема при использовании Arraylists, и это происходит из-за того, что длина (размер) Arraylist может измениться. При удалении изменяется размер; поэтому после первой итерации ваш код переходит в режим haywire. Лучший совет - либо использовать Iterator, либо зацикливать со спины, я рекомендую цикл backword, хотя, поскольку я считаю, что он менее сложный и он по-прежнему отлично работает с многочисленными элементами:
//Let decrement!
for(int i = size-1; i >= 0; i--){
if (data.get(i).getCaption().contains("_Hardi")){
data.remove(i);
}
}
По-прежнему ваш старый код, только зацикленный по-другому!
Надеюсь, это поможет...
Веселая кодировка!!!