Java集合之contains与元素删除(remove)

本文详细解析了Java集合中contains方法的源码,强调了equals方法的重要性。接着讨论了remove元素的两种方式,包括集合自带的remove方法和Iterator的remove方法。深入到ArrayList的源码中,分析了Iterator的remove操作以及在并发修改情况下可能出现的ConcurrentModificationException异常。内容包含源码分析及实例说明。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

# 一、contains源码解析

public boolean contains(Object o) {
        return indexOf(o) >= 0;
    }
public int indexOf(Object o) {
        if (o == null) {
            for (int i = 0; i < size; i++)
                if (elementData[i]==null)
                    return i;
        } else {
            for (int i = 0; i < size; i++)
                if (o.equals(elementData[i]))
                    return i;
        }
        return -1;
    }

通过源码,当传入的对象的引用不为null时,调用Object的equals方法比较两个对象的地址。
elementData[i]存的是对象的地址。
如果传入的对象,重写了equal方法,则按照对象重写后的equals规则进行比较。

//解析:ArrayList的底层数据结构是数组,当第一个元素加到ArrayList中时,ArrayList的容量扩展到默认数值10
Object[] elementData;
 /**
     * The array buffer into which the elements of the ArrayList are stored.
     * The capacity of the ArrayList is the length of this array buffer. Any
     * empty ArrayList with elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA
     * will be expanded to DEFAULT_CAPACITY when the first element is added.
     */
 private static final int DEFAULT_CAPACITY = 10;

例一:比较包装类

//Integer重写了equals方法,比较的是内容。
Integer a = new Integer(100);
collection.add(a);
Integer b = new Integer(100);
System.out.println(collection.contains(b));//输出true

例二:比较自定义类

//自定义Manager类
public class Manager {
    private  String name;
    private int no;
    public Manager(int no,String name){
        this.name=name;
        this.no = no;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getNo() {
        return no;
    }
    public void setNo(int no) {
        this.no = no;
    }
    public boolean equals(Object o){
        if(this==o){
            return true;
        }
        if(o instanceof Manager){
            Manager manager = (Manager) o;
            if(this.no == manager.no&& this.name.equals(manager.name)){
                return true;
            }
        }
        return false;
    }
}
Collection collection = new ArrayList();
Manager e = new Manager(100,"lisan");
collection.add(e);
Manager f = new Manager(100,"lisan");
System.out.println(collection.contains(f));//输出true

总结

Object的equals方法比较的是对象内存地址,在实际的业务逻辑中,不能比较地址,因此contains和remove方法都需要将集合中的对象重写equals方法。

二、remove方法

集合元素删除的方式有:
(1)集合自带的remove方法
(2)迭代器(Iterator)的remove方法
目的都是删除集合的元素。

 Collection collection =new ArrayList();
        collection.add(1);
        collection.add(2);
        collection.add(3);

        /**
         * 创建集合的迭代器
         */
        Iterator iterator = collection.iterator();
        while (iterator.hasNext()){//迭代器遍历集合
            System.out.println(iterator.next());//输出集合的元素
            iterator.remove();//删除集合的元素
            System.out.println("collection.size():"+collection.size());
        }

-------------------------------
1
collection.size()2
2
collection.size()1
3
collection.size()0

三、 Iterator之remove源码解析

首先看ArrayList的iterator()方法的具体实现,查看源码发现在ArrayList的源码中并没有iterator()这个方法,那么很显然这个方法应该是其父类或者实现的接口中的方法,我们在其父类AbstractList中找到了iterator()方法的具体实现,下面是其实现代码:

public Iterator<E> iterator() {
    return new Itr();
}

从这段代码可以看出返回的是一个指向Itr类型对象的引用,我们接着看Itr的具体实现,在AbstractList类中找到了Itr类的具体实现,它是AbstractList的一个成员内部类,下面这段代码是Itr类的所有实现:

private class Itr implements Iterator<E> {
    int cursor = 0;
    int lastRet = -1;
    int expectedModCount = modCount;
    public boolean hasNext() {
           return cursor != size();
    }
//如果下一个访问的元素下标不等于ArrayList的大小,就表示有元素需要访问,这个很
//容易理解,如果下一个访问元素的下标等于ArrayList的大小,则肯定到达末尾了。
    public E next() {
           checkForComodification();
        try {
        E next = get(cursor);//得到元素
        lastRet = cursor++;//cursor加1
        return next;//返回值
        } catch (IndexOutOfBoundsException e) {
        checkForComodification();
        throw new NoSuchElementException();
        }
    }

    public void remove() {
        if (lastRet == -1)
        throw new IllegalStateException();
           checkForComodification();
 
        try {
        AbstractList.this.remove(lastRet);
        if (lastRet < cursor)
            cursor--;
        lastRet = -1;
        expectedModCount = modCount;
        } catch (IndexOutOfBoundsException e) {
        throw new ConcurrentModificationException();
        }
    }
    final void checkForComodification() {
        if (modCount != expectedModCount)
        throw new ConcurrentModificationException();
    }
}

/**
cursor:表示下一个要访问的元素的索引,从next()方法的具体实现就可看出
lastRet:表示上一个访问的元素的索引
expectedModCount:表示对ArrayList修改次数的期望值,它的初始值为modCount。
modCount是AbstractList类中的一个成员变量,默认值为0
**/

四、ArrayList中之remove源码解析

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 void fastRemove(int index) {
    modCount++;
    int numMoved = size - index - 1;
    if (numMoved > 0)
        System.arraycopy(elementData, index+1, elementData, index,
                numMoved);
    elementData[--size] = null; // Let gc do its work
    //将引用置为null以方便垃圾收集器进行回收工作
    //总结:个人写代码需要及时将不使用的对象赋值为null
}

通过remove方法删除元素最终是调用的fastRemove()方法。
在fastRemove()方法中,首先对modCount进行加1操作(因为对集合修改了一次),然后接下来就是删除元素的操作,最后将size进行减1操作,并将引用置为null以方便垃圾收集器进行回收工作。

注意此时各个变量的值:对于iterator,其expectedModCount为0,cursor的值为1,lastRet的值为0。对于list,其modCount为1,size为0。

接着看,执行完删除操作后,继续while循环,调用hasNext方法()判断,由于此时cursor为1,而size为0,那么返回true,所以继续执行while循环,然后继续调用iterator的next()方法。
注意,此时要注意next()方法中的第一句:checkForComodification()。

在checkForComodification方法中进行的操作是:

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

如果modCount不等于expectedModCount,则抛出ConcurrentModificationException异常。
很显然,此时modCount为1,而expectedModCount为0,因此程序就抛出了ConcurrentModificationException异常。

因此:在迭代器中调用list.remove()方法导致modCount和expectedModCount的值不一致,从而出现异常。

参考资料:
  http://blog.youkuaiyun.com/izard999/article/details/6708738
  http://www.2cto.com/kf/201403/286536.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值