java入门(二十九)

并发修改异常

  • 需求:有一个集合,里面有三个元素。遍历集合,得到每一个元素,看有没有"world"这个元素,如果有,九田家一个“javee”的元素
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class LIstDemo {
    public static void main(String[] args) {
        // 创建集合对象
        List<String> list = new ArrayList<String>();
        //添加元素
        list.add("hello");
        list.add("world");
        list.add("java");
        //遍历集合得到每一个元素,看有没有"world"这个元素,如果有,我就添加一个"javaee"元素
        Iterator<String> it = list.iterator();
        while (it.hasNext()) {
            String s = it.next();
            if(s.equals("world")){
                list.add("javaee");
            }
        }
        //输出集合对象
        System.out.println(list);
    }
}
  • 得到一个异常
    在这里插入图片描述
  • java.util.ConcurrentModificationException 。这是个运行时异常,当不允许这种修改,可以通过检测到对象的并发修改方法来抛出此异常。
    在这里插入图片描述
  • 通过源码分析讲解并发修改异常。
    在这里插入图片描述
  • ArraysList继承了一个类,然后实现了一个接口
public class ArrayList<E> extends AbstractList<E> implements List<E>
  • 他要实现这个接口,就要重写两个方法。
  • 源码基本如下
public interface List<E> {
    Iterator<E> iterator();
    boolean add(E e);
}
public class ArrayList<E> extends AbstractList<E> implements List<E>{
    public boolean add(E e) {
        modCount++;
        add(e, elementData, size);
        return true;
    }
    public Iterator<E> iterator() {
        return new Itr();
    }
    private class Itr implements Iterator<E> {
        int expectedModCount = modCount;
        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];
        }
        final void checkForComodification() {
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();
       }
}
  • 我们出问题最终是在next()方法中出的问题,为什么呢?在next()方法下首先调用了checkForComodification()方法。而在控制台看到的也是他出了问题。在这里面做了个判断,如果这两个值不相等,就会抛出并发修改异常。
        final void checkForComodification() {
            if (modCount != expectedModCount)   
                throw new ConcurrentModificationException();
       }
  • modCount 这个是修改集合的次数,expectedModCount是预期修改集合的次数,如果他俩不相等就会抛出并发修改异常。一进来的时候,是将实际修改值赋值给预期修改值,所以他们应该是相等的
    private class Itr implements Iterator<E> {
        int expectedModCount = modCount;
  • 当你做了某个操作,他俩值不一样,就抛出了并发修改异常。modCount来自ArrayList继承的父类里面
public abstract class AbstractList<E>{
	protected int modCount = 0;
}
  • 我们写的报错的程序中,写了一旦s.equals(“world”),就会list.add(“javaee”)。实际修改集合做了一个++,但是预期修改集合的次数没有做++。你下次调用next方法的时候肯定会走checkForComodification方法,判断到不一样,就会抛出并发修改异常。
    实际
  • 遍历集合可以通过for循环遍历
        for(int i =0 ;i<list.size();i++){
            String s = list.get(i);
            if(s.equals("world")){
                list.add("javaee");
            }
        }
  • 那么在上面方法list.get没有做判断吗?来看一下源码。在get方法里面,并没有做实际修改值和预期修改值的判断,他只是做了一个根据索引获取元素。
    public E get(int index) {
        rangeCheck(index);

        return elementData(index);
    }
  • 并发修改异常产生的原因:迭代器遍历过程中,通过集合对象修改了集合中元素的长度,造成了迭代器获取元素中判断预期修改值和实际修改值不一致。
  • 解决方案是用for循环遍历,然后用集合对象做对应的操作

ListIterator

  • ListIterator被称为列表迭代器。通过List集合的listIterator()方法得到。
    在这里插入图片描述
  • 通过这个方法返回列表的迭代器。用于允许程序员沿任一方向遍历列表的列表和迭代器,在列表迭代期间修改列表,并获取列表中迭代器的当前位置
  • 他有 hasNext() ,next() ,hasPrevious(),previous() 方法。还有add方法,把指定的元素插入列表,迭代器可以往集合中直接添加元素的。
    在这里插入图片描述
  • next和hasNext方法是继承自iterator的。previous和hasPrevious是逆向遍历的。
import java.util.List;
import java.util.ArrayList;
import java.util.ListIterator;

public class ListIteratorDemo {
    public static void main(String[] args) {
        //创建集合对象
        List<String> list = new ArrayList<String>();
        //添加元素
        list.add("hello");
        list.add("world");
        list.add("java");
        //通过list集合的listIterator()方法得到
        ListIterator<String> lit = list.listIterator();
        while(lit.hasNext()){
            String s = lit.next();
            System.out.println(s);
        }
    }
}

在这里插入图片描述

  • 逆向输出
        //逆向遍历
        while (lit.hasPrevious()){
            String s = lit.previous();
            System.out.println(s);
        }

在这里插入图片描述

  • 正向的时候很少使用列表迭代器,会直接使用iterator。这里通过列表迭代器的add添加元素。
        ListIterator<String> lit = list.listIterator();
        while(lit.hasNext()){
            String s = lit.next();
            if(s.equals("world")){
                lit.add("javaee");
            }
        }
        System.out.println(list);

在这里插入图片描述

  • 来看一下ListIterator的源码
public interface List<E> {
    Iterator<E> iterator();
    ListIterator<E> listIterator();
    boolean add(E e);
}
public class AbstractList<E>{
    protected int modCount = 0;
}
int modCount = 0;
public class ArrayList<E> extends AbstractList<E> implements List<E>{
    public E get(int index) {
        rangeCheck(index);

        return elementData(index);
    }
    public boolean add(E e) {
        modCount++;
        add(e, elementData, size);
        return true;
    }
    public Iterator<E> iterator() {
        return new Itr();
    }
    private class Itr implements Iterator<E> {

    }
    public ListIterator<E> listIterator() {
        return new ListItr(0);
    }
        private class ListItr extends Itr implements ListIterator<E> {
            ListItr(int index) {
                super();
                cursor = index;
            }
    
            public boolean hasPrevious() {
                return cursor != 0;
            }
    
            public int nextIndex() {
                return cursor;
            }
    
            public int previousIndex() {
                return cursor - 1;
            }
    
            @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];
            }
    
            public void set(E e) {
                if (lastRet < 0)
                    throw new IllegalStateException();
                checkForComodification();
    
                try {
                    ArrayList.this.set(lastRet, e);
                } catch (IndexOutOfBoundsException ex) {
                    throw new ConcurrentModificationException();
                }
            }
    
            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();
                }
            }
        }

}
  • ListITR继承自ITR,实现了ListIterator这个接口,所以说在调用listIterator这个方法得到的是ListIterator实现类的对象。
  • add方法重点看 expectedModCount = modCount这句,把实际修改值会赋值给预期修改值,列表迭代器add方法在添加了元素之后,调用next判断的时候,这两个值是一致的,所以不会出现并发修改异常,因为底层最终把实际修改值赋给预期修改值。

增强for循环

  • 增强for循环是用来简化数组和和Collection集合的遍历
  • Collection集合继承了Iterable接口
    在这里插入图片描述
  • Iterable接口,实现接口允许对虾是哪个成为foreach语句的目标。所以Collection体系的都可以成为foreach的目标。其内部原理是一个Iterator迭代器。
    在这里插入图片描述
  • 格式
for(元素数据类型 变量名 : 数组或者Collection集合) {
	//在此处使用变量即可,该变量就是元素
}
  • 范例
int []  arr  =  { 1,2,3,4,5}
for(int i : arr){
  System.out.println(i);
}

在这里插入图片描述

  • 字符串类型的数组也可以
        String[] strArray = {"hello","world","java"};
        for (String s : strArray){
            System.out.println(s);
        }

在这里插入图片描述

  • 试一下集合,也是正常输出的。
        List<String> list = new ArrayList<String>();
        list.add("hello");
        list.add("world");
        list.add("java");
        for(String s : list){
            System.out.println(s);
        }
  • 如何验证他内部是一个Iterator迭代器呢?迭代器遍历集合,然后判断里面元素有没有world。如果有,通过结合调用add方法去往集合中添加元素,这时候就会抛出一个并发修改异常。如果异常,它内部就是一个Iterator迭代器。
        for(String s : list){
            if(s.equals("world")){
                list.add("javaee");
            }
        }

在这里插入图片描述

案例:List集合存储学生对象用三种方式遍历

  • 需求:创建一个存储学生对象集合,存储3个学生对象,使用程序实现在控制台遍历该集合。
  1. 定义学生类
  2. 创建List集合对象
  3. 创建学生对象
  4. 把学生添加到集合
  5. 遍历集合:①迭代器–集合特有的②普通for–带有索引的③增强for–最方便的
  • 学生类
public class Student {
    private String name;
    private int age;
    public Student(){};
    public Student(String name,int age){
        this.name = name;
        this.age = age;
    }
    public void setName(String name){
        this.name = name;
    }
    public String getName(){
        return name;
    }
    public void setAge(int age){
        this.age = age;
    }
    public int getAge(){
        return age;
    }
}
  • 主方法
import java.util.ArrayList;
import java.util.List;

public class ListDemo {
    public static void main(String[] args) {
        //创建List对象
        List<Student> list = new ArrayList<Student>();
        //创建学生对象
        Student s1 = new Student("林青霞",30);
        Student s2 = new Student("张曼玉",35);
        Student s3 = new Student("王祖贤",33);
        //把学生添加到集合
        list.add(s1);
        list.add(s2);
        list.add(s3);
    }
}
  • 迭代器
        Iterator<Student> it = list.iterator();
        while(it.hasNext()){
            Student s = it.next();
            System.out.println(s.getName()+","+s.getAge());
        }
  • 普通的for
        for (int i = 0 ; i < list.size() ; i++){
            Student s = list.get(i);
            System.out.println(s.getName()+","+s.getAge());
        }
  • 增强for
        for(Student s : list){
            System.out.println(s.getName()+","+s.getAge());
        }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值