数据结构之集合之迭代器并发修改异常与逆序遍历之源码剖析
数据结构之集合
集合
单列集合 Collection
List接口(元素不唯一存储有顺序)
ArrayList(具体类)
LinkedList(具体类)
Set接口(元素唯一存储无顺序)
HashSet(具体类)
双列集合 Map
Map接口(以键值对的形式存储元素,键唯一,值不唯一)
HashMap(具体类)
集合元素
集合是存储对象的,由于对象具有多态等特点,每次把元素取出肯定是需要向下转型
但是这个要转的类型并不确定,于是这个强转的过程就有出错的可能,怎么办呢?
那么为了解决这个问题,就需要用到泛型
泛型:
定义
泛型用来规定这个集合只能存放某个类型的数据
符号
<>
使用泛型来规定这个集合只能存放某个类型的数据,就不需要关心强转的问题了
[code]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方法
迭代器并发修改异常
测试代码
[code]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); // } } }
源码分析
[code]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 ? get(i)==null : 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; 20000 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可以实现逆向遍历,但是要求先正向遍历,才能逆向遍历
[code]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); } } }
源码分析
[code]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<>());
- 集合学习总结2-List - --迭代器遍历的时候不能添加,会爆并发修改异常
- java.util.ConcurrentModificationException ,遍历集合并同时修改集合,并发造成的异常解决办法
- List集合遍历时修改元素出现并发修改异常总结
- Java之集合框架 List接口的特有方法、迭代器的并发修改异常以及LinkedList特有方法
- java里面在遍历集合的时候对集合进行添加或者删除修改时的并发修改异常
- Java基础知识强化之集合框架笔记19:List集合迭代器使用之 并发修改异常的产生原因 以及 解决方案
- 使用迭代器遍历集合时,当集合中的数据发生变化是会抛出java.util.ConcurrentModificationException异常
- Java集合迭代器之fail-fast机制: 关于java集合的遍历以及ConcurrentModificationException(并发操作异常)
- 深入分析集合并发修改异常(源码分析)java.util.ConcurrentModificationException
- List和Map集合的使用及使用迭代器遍历数据
- redis 源码学习(核心数据结构剖析)
- 使用迭代器遍历List的时候修改List报ConcurrentModificationException异常原因分析
- 在多线程的情况下是由Iterator遍历修改集合对象,报ConcurrentModificationException()异常的根因分析
- Java学习篇之迭代器并发修改异常问题
- Java集合之ConcurrentModificationException(并发修改异常)分析
- 使用迭代器遍历List的时候修改List报ConcurrentModificationException异常的解决办法
- 使用迭代器遍历List的时候修改List报ConcurrentModificationException异常原因分析
- 迭代器模式:将遍历集合的任务交给一个叫做迭代器的对象,它的工作时遍历并选择序列中的对象,而客户端程序员不必知道或关心该集合序列底层的结构*/
- TCMalloc的使用与源码剖析之五---------TCMalloc中涉及到的几个重要的数据结构
- 集合框架(并发修改异常的产生原因及其解决方案)