ArrayList的remove、序列化(一)
2016-07-11 13:57
267 查看
ArrayList作为常用的集合,经常使用,这里有两个需要注意一下的地方,分别是remove方法和序列化操作。
在第一个删除元素操作之后,如果存在下一个待检查元素,则会抛出著名的ConcurrentModificationException,并且此处仍然没有避免上一个示例的错误区,使用for-each方式即使没有抛出异常,也会忽略掉已删除元素的下一个元素,例如:
避免忽略检查元素很简单,倒序判断即可,此处重点观察ConcurrentModificationException异常。
使用for-each方式遍历元素,即为使用Iterator迭代查询,在说Iterator之前,先看一下ArrayList中的remove方法。
2.将返回类型修改为boolean类型
差别神马的我们不关心,只关心共同点:
最后多说一句,此处ArrayList的元素类型为String,如果是Integer类,可能存在一个小的情况:
remove
先举个小示例:public class t{ public static void main(String[] args){ ArrayList<String> arr=new ArrayList<String>(); arr.add("a"); arr.add("b"); arr.add("c"); for(int i=0;i<arr.size();i++){ if(arr.get(i).equals("a")||arr.get(i).equals("b")){ arr.remove(i); } } System.out.println(arr.toString()); } } //输出结果:[b,c]原因很简单,大家都知道是因为元素虽然被删除,但是访问下标依然在递增。避免方式就是改为由后向前遍历查询删除即可。
另一种出错方式:
public class t{ public static void main(String[] args){ ArrayList<String> arr=new ArrayList<String>(); arr.add("a"); arr.add("b"); arr.add("c"); for(String s:arr){ if(s.equals("a")||s.equals("b")){ System.out.println("before:"+s); arr.remove(s); System.out.println("after:"+s); } } System.out.println(arr.toString()); } }//抛出异常
在第一个删除元素操作之后,如果存在下一个待检查元素,则会抛出著名的ConcurrentModificationException,并且此处仍然没有避免上一个示例的错误区,使用for-each方式即使没有抛出异常,也会忽略掉已删除元素的下一个元素,例如:
public class t{ public static void main(String[] args){ ArrayList<String> arr=new ArrayList<String>(); arr.add("a"); arr.add("b"); arr.add("c"); for(String s:arr){ if(s.equals("c")||s.equals("b")){ System.out.println("before:"+s); arr.remove(s); System.out.println("after:"+s); } } System.out.println(arr.toString()); } } //最后输出为:[a,c]
避免忽略检查元素很简单,倒序判断即可,此处重点观察ConcurrentModificationException异常。
使用for-each方式遍历元素,即为使用Iterator迭代查询,在说Iterator之前,先看一下ArrayList中的remove方法。
ArrayList中有两个remove方法:
public E remove(int index) { rangeCheck(index); modCount++;//修改次数 +1 E oldValue = elementData(index); int numMoved = size - index - 1; if (numMoved > 0) System.arraycopy(elementData, index+1, elementData, index, numMoved); //System类中的一个本地方法复制 elementData[--size] = null; // 删除数组最后一个元素并修改大小 return oldValue; } public boolean remove(Object o) { if (o == null) { for (int index = 0; index < size; index++) if (elementData[index] == null) { fastRemove(index); //调用fastRemove修改 return true; } } else { for (int index = 0; index < size; index++) if (o.equals(elementData[index])) { fastRemove(index); //调用fastRemove修改 return true; } } return false; }使用下标删除元素,modCount++修改次数加一,使用查找对象删除,调用fastRemove方法
private void fastRemove(int index) { modCount++; //修改计数+1 int numMoved = size - index - 1; if (numMoved > 0) System.arraycopy(elementData, index+1, elementData, index, numMoved);//System类中的一个本地方法复制 elementData[--size] = null; // 删除数组最后一个元素并修改大小 }此时比较remove(int index)与remove(Object o)+fastRemove(int index)
可以很明显看出,前后两种方式差别只有两处:
1.后者的删除多了一个查找定位index2.将返回类型修改为boolean类型
差别神马的我们不关心,只关心共同点:
都有:modCount++
ArrayList中for-each循环下的remove操作使得modCount发生了变化,for-each是一个Iterator迭代,Iterator接口中只有三个方法:public interface Iterator<E> { boolean hasNext(); E next(); void remove(); }ArrayList中使用迭代的方式为一个实现Iterator接口的内部类:
public Iterator<E> iterator() { return new Itr(); } private class Itr implements Iterator<E> { public boolean hasNext() { return cursor != size; } public E next() { checkForComodification(); ...................... } public void remove() { ............... checkForComodification(); ............... } final void checkForComodification() { if (modCount != expectedModCount) throw new ConcurrentModificationException(); } }Itr类中增加了一个checkForComodification方法,比较modCount与expectedModCount的值,使用ArrayList的两个remove方法都只是修改了modCount,所以在迭代hasNext返回true时,next方法会抛出异常。修改方式很简单,使用Iterator的remove方法即可,remove函数为:
public void remove() { if (lastRet < 0) throw new IllegalStateException(); checkForComodification(); try { ArrayList.this.remove(lastRet); cursor = lastRet; lastRet = -1; expectedModCount = modCount;//修改expectedModCount值 } catch (IndexOutOfBoundsException ex) { throw new ConcurrentModificationException(); } }使用示例:
public class t{ public static void main(String[] args){ ArrayList<String> arr=new ArrayList<String>(); arr.add("a"); arr.add("b"); arr.add("c"); Iterator<String> it=arr.iterator(); while(it.hasNext()){ String s=it.next(); if(s.equals("c")||s.equals("b")){ System.out.println("before:"+s); it.remove(); System.out.println("after:"+s); } } System.out.println(arr.toString()); } } //最后输出为:[a]
最后多说一句,此处ArrayList的元素类型为String,如果是Integer类,可能存在一个小的情况:
public class t{ public static void main(String[] args){ ArrayList<Integer> arr=new ArrayList<Integer>(); arr.add(2); arr.add(1); arr.add(3); arr.remove(2);//基本类型使用 remove(int index); 删除元素:3 arr.remove((Integer)2);//使用remove(Object o);删除元素:2 } }
总结:
如果遍历中用到ArrayList的remove时,使用倒序查询删除即可。使用for-each图操作简化的话,就别有remove行为了,最简单还是直接使用Iterator的删除操作,免得写个遍历还要顾虑许多。相关文章推荐
- java对世界各个时区(TimeZone)的通用转换处理方法(转载)
- java-注解annotation
- java-模拟tomcat服务器
- java-用HttpURLConnection发送Http请求.
- java-WEB中的监听器Lisener
- Android IPC进程间通讯机制
- Android Native 绘图方法
- Android java 与 javascript互访(相互调用)的方法例子
- 介绍一款信息管理系统的开源框架---jeecg
- 聚类算法之kmeans算法java版本
- java实现 PageRank算法
- 谷歌 Project Zero 团队宣布新政策,漏洞披露前将有完整的 90 天缓冲期
- PropertyChangeListener简单理解
- c++11 + SDL2 + ffmpeg +OpenAL + java = Android播放器
- 插入排序
- 冒泡排序
- 堆排序
- 快速排序