您的位置:首页 > 理论基础 > 数据结构算法

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

2019-03-26 12:53 411 查看

                                                                                     数据结构之集合

集合

    单列集合 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&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;

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

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐