集合——ArrayList

本文深入解析ArrayList在Java中的使用,包括其能否存储空值、线程安全性、动态扩容影响及增删查效率。并与线程安全的Vector进行对比,探讨了ArrayList的优缺点,以及为何推荐使用ArrayList而非Vector。
摘要由CSDN通过智能技术生成

ArrayList是java最常见的一个集合,是经常要用到的一个集合,这里详细讲一下。
ArrayList是基于数组实现的一个List类,封装了一个动态的,允许再分配的Object数组。
这里重点关注ArrayList的四个方面:
1.能不能存储空值 -》能
2.是否线程安全
3.集合的动态扩容对性能的影响
4.增删改查的效率

先从源码上看ArrayList的增删改查方法

添加操作

public boolean add(E e) {
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        elementData[size++] = e;
        return true;
    }

第二行的方法是确保ArrayList大小足够,给ArrayList扩容的方法,后面再分析。
ArrayList是直接将元素添加到集合中,非常方便

插入操作

public void add(int index, E element) {
        rangeCheckForAdd(index);

        ensureCapacityInternal(size + 1);  // Increments modCount!!
        System.arraycopy(elementData, index, elementData, index + 1,
                         size - index);
        elementData[index] = element;
        size++;
    }

首先检查index是否超出索引,超出则抛出异常,然后进行扩容。
值得关注的是,ArrayList插入元素前需要将数组进行一遍复制,然后将element放在数组index位置的后面。

删除操作

ArrayList中有两种删除操作,一种是通过index删除,一种是通过object删除。

public E remove(int index) {
        rangeCheck(index);

        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;
    }

通过下标删除,操作与插入操作类似,对index判断,然后将指定元素后面的数组进行复制,让整体向前移动一个位置,且将最后一个元素置为null,便于gc去回收。

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;
    }

通过对象删除,是遍历一遍数组,找到与object相等的元素,进行删除,看得出效率还是慢的。
fastmove()方法是执行跟上面的删除方法是一样的操作,将指定元素后的数组整体向前移动一个位置,且将最后一个元素置为null。
并且ArrayList能够存储重复值,也就是说,如果ArrayList有多个与object相等的元素,remove会删除index最小的元素。

查找操作
ArrayList可以通过index查找,也可以通过object查找

public E get(int index) {
        rangeCheck(index);

        return elementData(index);
    }

通过index查找是非常快的一个操作,只需要先检查index,然后返回值,这是ArrayList的优点。

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;
    }

通过object查找跟前面的类似,遍历整个数组,找到首个等于object的元素并返回。

优缺点总结
优点:
1.ArrayList的get()查找操作只需要对index判断,然后返回值,效率很快
缺点:
1.删除操作在极端情况下需要移动整个数组,效率比较低
2.插入操作在极端情况下需要移动整个数组,效率比较低

ArrayList和Vector的比较

线程安全
两者是最大区别是:ArrayList是线程不安全的,Vector是线程安全的。
要使ArrayList成为线程安全的,可以使用Collections类提供的synchronized()方法,

ArrayList a = Collections.synchronizedList(new ArrayList<>());

这样可以使得ArrayList 成为线程安全的。

扩容

ArrayList扩容方法

private void grow(int minCapacity) {
        // overflow-conscious code
        int oldCapacity = elementData.length;
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity;
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            newCapacity = hugeCapacity(minCapacity);
        // minCapacity is usually close to size, so this is a win:
        elementData = Arrays.copyOf(elementData, newCapacity);
    }

    private static int hugeCapacity(int minCapacity) {
        if (minCapacity < 0) // overflow
            throw new OutOfMemoryError();
        return (minCapacity > MAX_ARRAY_SIZE) ?
            Integer.MAX_VALUE :
            MAX_ARRAY_SIZE;
    }

首先将ArrayList的新容量等于旧容量的1.5倍,判断大小是否超过Integer的最大值,然后将数组复制一遍。
至于为什么是1.5倍
1.扩容太大必然浪费资源
2.扩容太小就得多次扩容,每次都得复制数组,影响性能
1.5倍的扩容应该是一个权衡利弊后合适的值

Vector的扩容方法
在这里插入图片描述
Vector初始化若不指定参数,大小会默认为10

private void grow(int minCapacity) {
        // overflow-conscious code
        int oldCapacity = elementData.length;
        int newCapacity = oldCapacity + ((capacityIncrement > 0) ?
                                         capacityIncrement : oldCapacity);
        if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity;
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            newCapacity = hugeCapacity(minCapacity);
        elementData = Arrays.copyOf(elementData, newCapacity);
    }

Vector在初始化时可以定义一个容量因子capacityIncrement,每次扩容都增加一个capacityIncrement,如果没有定义容量因子,vector的容量每次会增加一倍。

总结
推荐使用ArrayList,而不推荐Vector,书上是这么写的。
写这篇文章参考了博客园:‘五月的仓颉’这位大神的文章,源码上有一点区别,应该是java版本的不同。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值