数据结构之集合之迭代器并发修改异常与逆序遍历之源码剖析

本文详细介绍了Java集合框架中的迭代器在并发修改时可能导致的异常,以及如何通过ListIterator实现逆序遍历。同时,文章探讨了如何将线程不安全的集合类转换为线程安全的集合类,利用Collections工具类的同步包装方法。
摘要由CSDN通过智能技术生成

                                                                                     数据结构之集合

集合

    单列集合 Collection
        List接口(元素不唯一存储有顺序)
            ArrayList(具体类)
            LinkedList(具体类)
        Set接口(元素唯一存储无顺序)
            HashSet(具体类)
            
    双列集合 Map
        Map接口(以键值对的形式存储元素,键唯一,值不唯一)
            HashMap(具体类)

 

集合元素

    集合是存储对象的,由于对象具有多态等特点,每次把元素取出肯定是需要向下转型    
    但是这个要转的类型并不确定,于是这个强转的过程就有出错的可能,怎么办呢?
    那么为了解决这个问题,就需要用到泛型

泛型:
    定义
        泛型用来规定这个集合只能存放某个类型的数据
    符号
        <>
    使用泛型来规定这个集合只能存放某个类型的数据,就不需要关心强转的问题了

public class GenericsTest {
	public static void main(String[] args) {
		//<>泛型:对集合存放的类型进行约束
		//创建泛型集合对象
		System.out.println("泛型集合");
		ArrayList<String> arr1 = new ArrayList<String>();
		arr1.add("泛型约束");
		String generics = arr1.get(0);
		System.out.println(generics);
		//创建无泛型集合对象
		System.out.println("无泛型集合");
		ArrayList arr = new ArrayList();
		//添加元素对象
		arr.add("hello");
		arr.add(10);
		arr.add('c');
		// 获取元素时需要强转
		String hello = (String) arr.get(0);
		System.out.println(hello);
		//转换的类型并不确定,于是这个强转的过程就有出错的可能
		//ClassCastException: 
		//java.lang.Integer cannot be cast to java.lang.String
		//String s = (String) arr.get(1);
		if(arr.get(1) instanceof String){
			String str = (String) arr.get(1);
			System.out.println("obj instanceof String->"+str);	
		}else{
			System.out.println("ClassCastException:java.lang.Integer cannot be cast to java.lang.String");
			System.out.println();
		}
		Iterator it = arr.iterator();
		while(it.hasNext()){
			Object obj = it.next();
			//System.out.println("obj->"+obj);
			if(obj instanceof String){
				String str = (String) obj;
				System.out.println("obj instanceof String->"+str);	
			}
			if(obj instanceof Integer){
				Integer i = (Integer) obj;
				System.out.println("obj instanceof Integer->"+i);
			}
			if(obj instanceof Character){
				Character c = (Character) obj;
				System.out.println("obj instanceof Character->"+c);
			}
		}
	}
}

迭代器:
    遍历集合元素的工具,通过迭代器来访问操作集合元素
    集合里面的类一般都实现Iterable接口并重写Iterator方法
    
    迭代器并发修改异常

测试代码

public class ConcurrentModificationExceptionTest {
	public static void main(String[] args){
		List<String> strList = new ArrayList<String>();
		strList.add("string1");
		strList.add("string2");
		strList.add("string3");
		strList.add("string4");
		strList.add("string5");
		strList.add("string6");
		Iterator<String> it = strList.iterator();

		// 操作方式1:while(Iterator);报错
//		while(it.hasNext()) {
//			//it.next()首先调用checkForComodification()方法
//			//判断modCount与expectedModCount是否相等
//			String s = it.next();// modCount != expectedModCount throw new ConcurrentModificationException();
//			if("string2".equals(s)) {
//				strList.remove(s);
//			}
//		}
		
		//访问倒数第二个不报错 
		while(it.hasNext()) {//cursor==size 结束循环
			String s = it.next();//return (E) elementData[4],cursor = 4 + 1
			if("string5".equals(s)) {
				strList.remove(s);
				System.out.println(s);//string5
				System.out.println(strList.size());//5
			}
		}
		
		// 解决方案1:使用Iterator的remove方法删除元素
		// 操作方式1:while(Iterator):不报错
		while(it.hasNext()) {
			String s = it.next();
			if("string2".equals(s)) {
				it.remove();
			}
		}
		
		// 操作方式2:foreach(Iterator);报错
//		for(String s : strList) {
//			if("string2".equals(s)) {
//				strList.remove(s);
//			}
//		}
		/* 加强for循环其实底层就是用迭代器进行遍历
		 * 反编译文件
		for (Iterator iterator = strList.iterator(); iterator.hasNext();)
		{
			String s = (String)iterator.next();
			if ("string2".equals(s))
				strList.remove(s);
		}
		*/
		
		// 解决方案2:不使用Iterator遍历,注意索引的一致性
		// 操作方式3:for(非Iterator);不报错;注意修改索引
		for(int i=0; i<strList.size(); i++) {
			String s = strList.get(i);
			if("string6".equals(s)) {
				strList.remove(s);
				i--;// 元素位置发生变化,修改i
			}
		}
		
		// 解决方案3:新建一个临时列表,暂存要删除的元素,最后一起删除
//		List<String> templist = new ArrayList<String>();
//		for (String s : strList) {
//		    if(s.equals("string2")) {
//		        templist.add(s);
//		    }
//		}
//		// 查看removeAll源码,其使用Iterator进行遍历
//		strList.removeAll(templist);
		
		// 解决方案4:使用线程安全CopyOnWriteArrayList进行删除操作
                //ArrayList 的一个线程安全的变体,其中所有可变操作(add、set 等等)
                //都是通过对底层数组进行一次新的复制来实现的
//		List<String> strList = new CopyOnWriteArrayList<String>();
//		strList.add("string1");
//		strList.add("string2");
//		strList.add("string3");
//		strList.add("string4");
//		strList.add("string5");
//		strList.add("string6");
//		Iterator<String> it = strList.iterator();
//		while (it.hasNext()) {
//		    String s = it.next();
//		     if (s.equals("string2")) {
//		    	 strList.remove(s);
//		    	 System.out.println(s);
//		    }
//		     System.out.println(s);
//		}
		
	}	
}

源码分析

public class ArrayList<E> extends AbstractList<E>
        implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{
    private static final long serialVersionUID = 8683452581122892189L;

    /**
     * The size of the ArrayList (the number of elements it contains).
     *
     * @serial
     */
    private int size;

    /**
     * Returns an iterator over the elements in this list in proper sequence.
     *
     * <p>The returned iterator is <a href="#fail-fast"><i>fail-fast</i></a>.
     *
     * @return an iterator over the elements in this list in proper sequence
     */
    public Iterator<E> iterator() {
        return new Itr();
    }

    /**
     * An optimized version of AbstractList.Itr
     */
    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;

        public boolean hasNext() {
            return cursor != size;
        }

        @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];
        }

        public void remove() {
            if (lastRet < 0)
                throw new IllegalStateException();
            checkForComodification();

            try {
                ArrayList.this.remove(lastRet);
                cursor = lastRet;
                lastRet = -1;
                expectedModCount = modCount;//总能保证expectedModCount等于modCount
            } catch (IndexOutOfBoundsException ex) {
                throw new ConcurrentModificationException();
            }
        }

        final void checkForComodification() {
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();
        }
    }

     /**
     * Removes the element at the specified position in this list.
     * Shifts any subsequent elements to the left (subtracts one from their
     * indices).
     *
     * @param index the index of the element to be removed
     * @return the element that was removed from the list
     * @throws IndexOutOfBoundsException {@inheritDoc}
     */
    public E remove(int index) {
        rangeCheck(index);

        modCount++;//会使expectedModCount不等于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;
    }

    /**
     * Removes the first occurrence of the specified element from this list,
     * if it is present.  If the list does not contain the element, it is
     * unchanged.  More formally, removes the element with the lowest index
     * <tt>i</tt> such that
     * <tt>(o==null&nbsp;?&nbsp;get(i)==null&nbsp;:&nbsp;o.equals(get(i)))</tt>
     * (if such an element exists).  Returns <tt>true</tt> if this list
     * contained the specified element (or equivalently, if this list
     * changed as a result of the call).
     *
     * @param o element to be removed from this list, if present
     * @return <tt>true</tt> if this list contained the specified element
     */
    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;
    }

    /*
     * Private remove method that skips bounds checking and does not
     * return the value removed.
     */
    private void fastRemove(int index) {
        modCount++;//会使expectedModCount不等于modCount
        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
    }
}

从源码中可以看到

ArrayList类的内部类迭代器Itr类中的remove方法与ArrayList类中的remove方法是不一样的
迭代器Itr类中的remove方法中的这行代码
expectedModCount = modCount;
总能保证expectedModCount等于modCount
所以通过迭代器修改元素不会抛出异常
而通过ArrayList类修改元素
ArrayList类的remove方法中的这行代码
modCount++;
会使expectedModCount不等于modCount
从而抛出异常

 

特殊迭代器——ListIterator

ListIterator可以实现逆向遍历,但是要求先正向遍历,才能逆向遍历
ListIterator:
        ListIterator<E> listIterator():返回此列表元素的列表迭代器
         public interface ListIterator<E>extends Iterator<E>
特有功能:
         E previous():返回列表中的前一个元素。
         boolean hasPrevious():如果以逆向遍历列表,列表迭代器有多个元素,则返回true
注意: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");
		
		ListIterator<String> lit = list.listIterator();
		while(lit.hasNext()){
			String s = lit.next();
			System.out.println(s);
		}
		System.out.println("--------------------------");
		//先正向遍历,才能逆向遍历
		while(lit.hasPrevious()) {
			String s = lit.previous();
			System.out.println(s);
		}
	}
}

源码分析

public class ArrayList<E> extends AbstractList<E>
        implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{
/**
     * An optimized version of AbstractList.Itr
     */
    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;

        public boolean hasNext() {
            return cursor != size;
        }

        @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;//cursor相当于指针 cursor+1 指向下一个元素
            return (E) elementData[lastRet = i];
        }

        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();
            }
        }

        final void checkForComodification() {
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();
        }
    }

    /**
     * An optimized version of AbstractList.ListItr
     */
    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;//cursor相当于指针 cursor-1 指向上一个元素
            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();
            }
        }
    }
}

不管是正序还是逆序都是用cursor这个指针来索引的
所以要逆序先要将cursor指针移到末尾

如何把一个线程不安全的集合类变成一个线程安全的集合类?

注意:
    线程安全是指支持同步,而不能解决并发访问冲突
    能解决并发访问冲突线程安全的集合类是极其特殊的
    
Collections工具类完全由在 collection上进行操作或返回 collection 的静态方法组成。
它包含在 collection 上操作的多态算法,即“包装器”
包装器返回由指定 collection 支持的新 collection,以及少数其他内容

Collections工具类
public  static <T> Collection<T> synchronizedCollection(Collection<T> c)
          返回指定 collection 支持的同步(线程安全的)collection。
static <T> List<T> synchronizedList(List<T> list)
          返回指定列表支持的同步(线程安全的)列表。
static <K,V> Map<K,V> synchronizedMap(Map<K,V> m)
          返回由指定映射支持的同步(线程安全的)映射。
static <T> Set<T> synchronizedSet(Set<T> s)
          返回指定 set 支持的同步(线程安全的)set。
static <K,V> SortedMap<K,V> synchronizedSortedMap(SortedMap<K,V> m)
          返回指定有序映射支持的同步(线程安全的)有序映射。
static <T> SortedSet<T> synchronizedSortedSet(SortedSet<T> s)
          返回指定有序 set 支持的同步(线程安全的)有序 set。
          
//线程安全的集合类
         Vector<String> v = new Vector<>();
         Hashtable<String, String> hs = new Hashtable<>();
        
//线程不安全的集合类
         ArrayList<String> list = new ArrayList<>();
        
//把一个线程不安全的集合类变成一个线程安全的集合类
         List<String> list2 = Collections.synchronizedList(list);
         Map<Object, Object> map = Collections.synchronizedMap(new HashMap<>());

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值