• List 中正确的增删操作


    这个为什么要单独说的原因是,在开发中的对数据库中的增删为最基本的,但是是不是写对了就尤为重要

    先来看代码:

     1 public void testLoopInList(){
     2         List<String> a = new ArrayList<String>();
     3         a.add("1");
     4         a.add("2");
     5         a.add("w");
     6         for (String string : a) {
     7             System.out.println(string);
     8         }
     9         
    10         for (String temp : a) {
    11             if("2".equals(temp)){
    12                 a.remove(temp);
    13             }
    14         }
    15         
    16         for (String string : a) {
    17             System.out.println(string);
    18         }

    输出:

    1
    2
    
    java.util.ConcurrentModificationException
        at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:859)
        at java.util.ArrayList$Itr.next(ArrayList.java:831)
        at test.Test_ForeachAndIterator.testLoopInList(Test_ForeachAndIterator.java:19)

    出现错误了

    原因:首先从错误中可以看出,是ArraList中的Iterator的checkForComodification()出现的错误,说明了foreach的实现原理其实就是实现了内部类I特Iterator来进行遍历的,其次为什么会出错呢?

    因为在ArrayList和ArrayList的内部类的Iterator中的都会存在remove的方法,而ArrayList和Iterator中都各自有自己的维持长度的变量,前者是modCount,后者是expectModCount,但是源码中ArrayList的remove方法是会改变modCount的值,但却不会直接同步到expectModCount的值的,而Iterator中时刻都插入了checkForComodification()方法来监测modCount是否与expectModCount相等,所以就会很容易出现异常错误,当然下面的代码也是错的

     1     public void testLoopInList(){
     2         List<String> a = new ArrayList<String>();
     3         a.add("1");
     4         a.add("2");
     5         
     6         for (String string : a) {
     7             System.out.println(string);
     8         }
     9         
    10         Iterator<String> it = a.iterator();
    11         while(it.hasNext()){
    12             if("1".equals(it.next())){
    13                 a.remove(it.next());
    14             }
    15         }
    16         
    17         for (String string : a) {
    18             System.out.println(string);
    19         }
    20     }

    错误原因同上

    所以在解决问题的关键就是要避免这个异常的出现,也就是时刻让modCount==expectModCount,所以就是使用iterator的remove方法,因为会有使两者相等的代码

    即下面:

     1 public void testLoopInList(){
     2         List<String> a = new ArrayList<String>();
     3         a.add("1");
     4         a.add("2");
     5         
     6         for (String string : a) {
     7             System.out.println(string);
     8         }
     9         
    10         Iterator<String> it = a.iterator();
    11         while(it.hasNext()){
    12             if("1".equals(it.next())){
    13                 it.remove();
    14             }
    15         }
    16         
    17         for (String string : a) {
    18             System.out.println(string);
    19         }
    20     }

    相关源码提上:

     1 private class Itr implements Iterator<E> {
     2    /**
     3     * Index of element to be returned by subsequent call to next.
     4     */
     5    int cursor = 0;
     6    /**
     7     * Index of element returned by most recent call to next or
     8     * previous.  Reset to -1 if this element is deleted by a call
     9     * to remove.
    10     */
    11    int lastRet = -1;
    12    /**
    13     * The modCount value that the iterator believes that the backing
    14     * List should have.  If this expectation is violated, the iterator
    15     * has detected concurrent modification.
    16     */
    17    int expectedModCount = modCount;
    18    public boolean hasNext() {
    19            return cursor != size();
    20    }
    21    public E next() {
    22            checkForComodification();
    23        try {
    24        E next = get(cursor);
    25        lastRet = cursor++;
    26        return next;
    27        } catch (IndexOutOfBoundsException e) {
    28        checkForComodification();
    29        throw new NoSuchElementException();
    30        }
    31    }
    32    public void remove() {
    33        if (lastRet == -1)
    34        throw new IllegalStateException();
    35            checkForComodification();
    36        try {
    37        AbstractList.this.remove(lastRet);
    38        if (lastRet < cursor)
    39            cursor--;
    40        lastRet = -1;
    41        expectedModCount = modCount;
    42        } catch (IndexOutOfBoundsException e) {
    43        throw new ConcurrentModificationException();
    44        }
    45    }
    46    final void checkForComodification() {
    47        if (modCount != expectedModCount)
    48        throw new ConcurrentModificationException();
    49    }

    ArrayList中的remove

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

    当然你说我不用for增强,就用普通的for ,就不会有创建Iterator的操作,进而就不会有Iterator的实时监测维护值得操作,也行啊,但还是要注意有坑啊! 比如我们来看下面的代码:

     1 List list = new ArrayList();
     2         Collections.addAll(list, 1, 2, 3, 4, 5);
     3         for(int i = 0; i < list.size(); ++i){
     4             int val = (int) list.get(i);
     5             
     6             // 需求是删除 3 和 4 两个元素
     7             if(3 == val || 4 == val){
     8                 list.remove(i);
     9             }
    10         }

    这是我们非常常见的写法,结果 为 :1 2 3 4

    奇怪了,4为甚没有删除掉,其实仔细想想 还是能相同的 删除了3之后 i = 3 size = 4,这时候,list.get返回值为5, 会跳过4

    所以 修改的话,两个 liter tips : 要么自己维护索引,要么反向循环

    1         for(int i = list.size() - 1; i > 0; --i){
    2             int val = (int) list.get(i);
    3             
    4             // 需求是删除 3 和 4 两个元素
    5                         if(3 == val || 4 == val){
    6                             list.remove(i);
    7                         }
    8         }
    1 for(int i = 0; i < list.size(); ++i){
    2             int val = (int) list.get(i);
    3             
    4             // 需求是删除 3 和 4 两个元素
    5             if(3 == val || 4 == val){
    6                 list.remove(i);
    7                 i --; // 删除后,索引也应该变化
    8             }
    9         }

    ok, that's all ...

  • 相关阅读:
    Centos-7修改yum源为国内的yum源
    让vim显示空格,tab字符,及vim多行注释
    centos查找未挂载磁盘格式化并挂载
    记一次“愉快”的lnmp环境的搭建
    Sublime text3 的安装【解决官网被墙问题】
    php 解决json_encode中文UNICODE转码问题
    linux 下 apache启动、停止、重启命令
    Android利用Fiddler进行网络数据抓包
    ecshop 去版权(前台)
    ecshop 去版权
  • 原文地址:https://www.cnblogs.com/AmoryWang-JavaSunny/p/7468324.html
Copyright © 2020-2023  润新知