您的位置:首页 > 编程语言 > Java开发

ArrayList的remove、序列化(一)

2016-07-11 13:57 267 查看
ArrayList作为常用的集合,经常使用,这里有两个需要注意一下的地方,分别是remove方法和序列化操作。

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.后者的删除多了一个查找定位index

2.将返回类型修改为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 函数 安全 arraylist