我是靠谱客的博主 俊逸菠萝,这篇文章主要介绍ArrayList循环删除元素的方法总结一、遍历List集合的三种方式二、循环删除元素问题及比较分析三、源码分析四、总结for循环删除与迭代器删除的区别:五、 ArrayList的迭代器的实现,现在分享给大家,希望可以做个参考。

一、遍历List集合的三种方式

JAVA中循环遍历list有三种方式:for循环、增强for循环(即foreach循环)、iterator遍历

  1. 普通的for循环
  2. foreach循环(增强的for循环)
  3. 迭代器Iterator

二、循环删除元素问题及比较分析

创建ArrayList:

List<String> list = new ArrayList<String>();
list.add("AA");
list.add("BBB");
list.add("CCCC");
list.add("DDDD");
list.add("EEE");

1. for循环索引删除

示例:删除长度为4的字符串元素。

复制代码
1
2
3
4
5
for (int i = 0; i < list.size(); i++) {     if (list.get(i).length() == 4) {         list.remove(i);     } }

验证输出结果:

复制代码
1
for (String s : list) System.out.print(s + ",");

输出结果为:AA,BBB,DDDD,EEE,

错误之处DDDD元素竟然没有删除掉。

问题分析

这种方式的问题在于,删除某个元素后,list的大小size发生了变化,而你的索引也在变化所以会导致你在遍历的时候漏掉某些元素。比如当你删除第1个元素后,继续根据索引访问第2个元素时,因为删除的关系后面的元素都往前移动了一位,所以实际访问的是第3个元素。<见源码分析>;不会报出异常,只会出现漏删的情况;如果只是删除一个元素,就break,可以使用这种方式。

适用场景

因此,这种方式可以用在删除特定的一个元素时使用,但不适合循环删除多个元素时使用。

2. foreach循环删除元素

删除一个元素之后,无论是否还有满足条件的元素,都必须跳出循环break,否则报出java.util.ConcurrentModificationException

因此,也只能用于删除一个元素。

复制代码
1
2
3
4
5
6
7
8
//删除元素后必须break跳出,否则报出异常 for (String s : list) {     if (s.length() == 4) {         list.remove(s);         break;     } } for (String s : list)   System.out.print(s + ",");//AA,BBB,DDDD,EEE,

这种方式的问题在于,删除元素后继续循环会报错误信息ConcurrentModificationException,因为元素在使用的时候发生了并发的修改,导致异常抛出。但是删除完毕马上使用break跳出,则不会触发报错。

3. 迭代器Iterator

使用迭代器删除元素完美。

复制代码
1
2
3
4
5
6
7
8
9
//迭代器:完美 Iterator<String> iterator = list.iterator(); while(iterator.hasNext()){     if(iterator.next().length()==4){         iterator.remove();     } } for (String s : list) System.out.print(s + ",");//AA,BBB,EEE,

这种方式可以正常的循环及删除。但要注意的是,使用iterator的remove方法,如果用list的remove方法同样会报上面提到的ConcurrentModificationException错误。

4. 其他方法:新建一个List对象

适合场景:对一个List对象,需要删除大量元素,保留较少元素时,此时可以采用创建一个新的List对象,将需要保留的元素add进新的List对象,然后让旧引用指向新对象即可。

新建一个List对象,将需要保留的元素item添加到新List中,然后原来的引用指向新List即可。

复制代码
1
2
3
4
5
6
7
8
9
// 4. 创建新对象 ArrayList<String> newList = new ArrayList<String>(); for (int i = 0; i < list.size(); i++) {     if (list.get(i).length() < 3) {//条件为需要保留元素的条件         newList.add(list.get(i));     } } list = newList; for (String s : list) System.out.print(s + ",");//AA,

三、源码分析

1. ArrayList中的size()方法

内部有个size属性,直接返回该size属性。

复制代码
1
2
3
4
public int size() {     checkForComodification();     return this.size; }

2. remove(index)与remove(Object)方法

  • remove(index)

原理:将index之后的元素向前移动1个位置;通过native方法-System.arraycopy实现。

并将size减一,并将原list最后一个元素引用置为null,便于垃圾回收GC。

复制代码
1
2
3
4
5
6
7
8
9
10
public E remove(int index) {     rangeCheck(index);     modCount++;     E oldValue = elementData(index);     int numMoved = size - index - 1;     if (numMoved > 0)         System.arraycopy(elementData, index+1, elementData, index,numMoved);     elementData[--size] = null; // clear to let GC do its work     return oldValue; }public static native void arraycopy(Object src,  int  srcPos,

                                                                   Object dest, int destPos, int length);

  • remove(Object)

内部有个for循环遍历,遍历一遍找到该元素的索引,然后再调用remove(index)方法删除。

因此,foreach删除元素性能肯定不如普通的for循环。

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public boolean remove(Object o) {     if (o == null) {         for (int index = 0; index < size; index++)             if (elementData[index] == null) {                 fastRemove(index);                 return true;             }     } else {         for (int index = 0; index < size; index++)             if (o.equals(elementData[index])) {                 fastRemove(index);                 return true;             }     }     return false; }

3. ArrayList的迭代器实现原理

见五、迭代器原理

迭代器内部也是通过调用remove(index)方法实现的,只不是增加了cursor控制了索引,保证删除元素后,cursor不变。

四、总结

如果只是删除一个元素,这4种方法都可以实现,但是普通的for循环和迭代器较好,因为遍历过程中索引是已知的;

若循环删除多个元素,只能使用迭代器和创建新对象存储。根据情况使用,一般情况下使用迭代器最好。

for循环删除与迭代器删除的区别:

迭代器删除:内部也是调用remove(index)方法,只不过是通过cursor控制了索引,在删除元素后cursor不变,不会造成漏删的情况。

与创建新ArrayList相比:创建新ArrayList会消耗更多的内存空间;在删除较多的情况下效率更高些。

五、 ArrayList的迭代器的实现

1. Iterator<E>接口

public interface Iterator<E>

定义了四种方法:hasNextnextremove、forEachRemaining

boolean hasNext();

E next();

default void remove() {

        throw new UnsupportedOperationException("remove");

}

default void forEachRemaining(Consumer<? super E> action) {

        Objects.requireNonNull(action);

        while (hasNext())

            action.accept(next());

}

迭代器的使用方法:hasNext和next方法结合使用

Iterator<String> iterator = list.iterator();
while (iterator.hasNext()) {
    if (iterator.next().length() == 4) {
        iterator.remove();
    }
}

2. ListIterator<E> extends Iterator<E>

由于List列表这种特殊的集合,可以前后遍历、添加、删除等,继承了Iterator<E>接口,并扩展了几个接口。

boolean hasNext();

E next();

 

boolean hasPrevious();

E previous();

 

int nextIndex();

int previousIndex();

 

void remove();

void set(E e);

void add(E e);

3. 实现方式:Iterator与ListIterator

Iterator<E>一般在集合类中作为内部类实现,由于方法很少,一般实现起来比较简单。

一般会设置几个索引属性cursorlastRet、expectedModCount等结合集合的size属性实现。

int cursor;       // index of next element to return
int lastRet = -1; // index of last element returned; -1 if no such
int expectedModCount = modCount;

下面以ArrayList的Iterator实现为例:

ArrayList提供了Iterator的实现类Itr();也提供了ListIterator的ListItr()。

  1. 对外API方法获取迭代器

  • 获取Iterator迭代器:
复制代码
1
2
3
public Iterator<E> iterator() {     return new Itr(); }
  • 获取listIterator列表迭代器:
复制代码
1
2
3
4
public ListIterator<E> listIterator() {     return new ListItr(0); }
复制代码
1
2
3
4
5
public ListIterator<E> listIterator(int index) {     if (index < 0 || index > size)         throw new IndexOutOfBoundsException("Index: "+index);     return new ListItr(index); }

2. 具体实现—循环删除元素的实现

(1)Itr类

复制代码
1
2
3
4
5
6
private class Itr implements Iterator<E> {     int cursor;       // index of next element to return     int lastRet = -1; // index of last element returned; -1 if no such     int expectedModCount = modCount;     Itr() {}
  • hasNext方法:只需要判断当前索引cursor是否到达末尾。
复制代码
1
2
3
public boolean hasNext() {         return cursor != size;     }
  • next方法:对光标cursor的判断,如果合法,返回当前元素,并cursor指向下一个

cursor自动增1,因此调用next方法之后,光标指向下一个元素。

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
    @SuppressWarnings("unchecked")     public E next() {         checkForComodification();         int i = cursor;         if (i >= size)             throw new NoSuchElementException();         Object[] elementData = ArrayList.this.elementData;         if (i >= elementData.length)             throw new ConcurrentModificationException();         cursor = i + 1;         return (E) elementData[lastRet = i];     }
  • remove方法:

真正删除的操作还是通过调用ArrayList的remove方法,改变的是索引,对外不暴露索引,删除之后,仍然保持cursor=lastRet不变,这就是与for循环删除的区别。

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
    public void remove() {         if (lastRet < 0)             throw new IllegalStateException();         checkForComodification();         try {             ArrayList.this.remove(lastRet);             cursor = lastRet;             lastRet = -1;             expectedModCount = modCount;         } catch (IndexOutOfBoundsException ex) {             throw new ConcurrentModificationException();         }     }
  • forEachRemaining方法
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
@Override @SuppressWarnings("unchecked") public void forEachRemaining(Consumer<? super E> consumer) {     Objects.requireNonNull(consumer);     final int size = ArrayList.this.size;     int i = cursor;     if (i >= size) {         return;     }     final Object[] elementData = ArrayList.this.elementData;     if (i >= elementData.length) {         throw new ConcurrentModificationException();     }     while (i != size && modCount == expectedModCount) {         consumer.accept((E) elementData[i++]);     }     // update once at end of iteration to reduce heap write traffic     cursor = i;     lastRet = i - 1;     checkForComodification(); }

(2). ListItr类

ListItr可以提供一个参数index,表示从哪个位置开始迭代;缺省情况下是从0开始。

复制代码
1
2
3
4
5
6
7
8
/**  * An optimized version of AbstractList.ListItr  */ private class ListItr extends Itr implements ListIterator<E> {     ListItr(int index) {         super();         cursor = index;     }
  • hasPrevious方法:
复制代码
1
2
3
    public boolean hasPrevious() {         return cursor != 0;     }
  • nextIndex方法:
复制代码
1
2
3
public int nextIndex() {         return cursor;     }
  • previousIndex
复制代码
1
2
3
public int previousIndex() {         return cursor - 1;     }
  • previous
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
 @SuppressWarnings("unchecked")     public E previous() {         checkForComodification();         int i = cursor - 1;         if (i < 0)             throw new NoSuchElementException();         Object[] elementData = ArrayList.this.elementData;         if (i >= elementData.length)             throw new ConcurrentModificationException();         cursor = i;         return (E) elementData[lastRet = i];     }
  • set方法
复制代码
1
2
3
4
5
6
7
8
9
10
public void set(E e) {         if (lastRet < 0)             throw new IllegalStateException();         checkForComodification();         try {             ArrayList.this.set(lastRet, e);         } catch (IndexOutOfBoundsException ex) {             throw new ConcurrentModificationException();         }     }
  • add方法
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
public void add(E e) {         checkForComodification();         try {             int i = cursor;             ArrayList.this.add(i, e);             cursor = i + 1;             lastRet = -1;             expectedModCount = modCount;         } catch (IndexOutOfBoundsException ex) {             throw new ConcurrentModificationException();         }     } }

感悟:之前总觉得迭代器很神秘,查看源码之后,发现so so easy。

 

 

 

 

 

 

最后

以上就是俊逸菠萝最近收集整理的关于ArrayList循环删除元素的方法总结一、遍历List集合的三种方式二、循环删除元素问题及比较分析三、源码分析四、总结for循环删除与迭代器删除的区别:五、 ArrayList的迭代器的实现的全部内容,更多相关ArrayList循环删除元素的方法总结一、遍历List集合的三种方式二、循环删除元素问题及比较分析三、源码分析四、总结for循环删除与迭代器删除的区别:五、内容请搜索靠谱客的其他文章。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(80)

评论列表共有 0 条评论

立即
投稿
返回
顶部