ArrayList的个人体会

本文探讨了Java中ArrayList的数据结构和主要方法,重点关注其扩容机制。ArrayList通过多个方法实现动态扩容,包括ensureCapacity、calculateCapacity、ensureExplicitCapacity和grow等。作者分享了自己简化版的扩容实现,并讨论了clone方法的浅拷贝特性。此外,还涉及了浅拷贝与深拷贝的概念及其区别。

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

ArrayList的个人体会

最近Java学习时,我们开始去看ArrayList的原码,接下来就让我来说一下我在看完原码后自己的一些心得体会。

首先,在ArrayList中定义了几个数据

private static final long serialVersionUID = 8683452581122892189L;
private static final int DEFAULT_CAPACITY = 10;
private static final Object[] EMPTY_ELEMENTDATA = {};
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
transient Object[] elementData;
private int size;

/**
* elementData 是承载器,用于储存数组元素
* DEFAULT_CAPACITY 是默认的容量大小
* EMPTY_ELEMENTDATA 和 DEFAULTCAPACITY_EMPTY_ELEMENTDATA 是两个空数组
* size 代表的不是 elementData 的长度,而是ArrayList的元素个数
* 第一行的 serialVersionUID 没有看懂,不知道有什么作用
*/

ArrayList的构造方法

public ArrayList(int initialCapacity) {
        if (initialCapacity > 0) {
            this.elementData = new Object[initialCapacity];
        } else if (initialCapacity == 0) {
            this.elementData = EMPTY_ELEMENTDATA;
        } else {
            throw new IllegalArgumentException("Illegal Capacity: "+
                                               initialCapacity);
        }
    }

    public ArrayList() {
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }

    public ArrayList(Collection<? extends E> c) {
        Object[] a = c.toArray();
        if ((size = a.length) != 0) {
            if (c.getClass() == ArrayList.class) {
                elementData = a;
            } else {
                elementData = Arrays.copyOf(a, size, Object[].class);
            }
        } else {
            // replace with empty array.
            elementData = EMPTY_ELEMENTDATA;
        }
    }

/**
* 第一个有参构造就是创建一个指定容量的数组链表
* 无参构造就是创建一个默认的空数组
* 第二个有参构造按照集合的迭代器返回元素的顺序构造包含指定集合元素的列表,即指定元素的初始化
*/

ArrayList 的大部分方法与 LinkedList 一致,add()remove()get()set() 啥的。我感觉,ArrayList 中,与 LinkedList 不同且我认为最重要的一部分方法就是 ArrayList 的扩容的方法。

ArrayList 的扩容方法总共有5个,原码如下

public void ensureCapacity(int minCapacity) {
        int minExpand = (elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA)
            ? 0
            : DEFAULT_CAPACITY;

        if (minCapacity > minExpand) {
            ensureExplicitCapacity(minCapacity);
        }
    }
	
    private static int calculateCapacity(Object[] elementData, int minCapacity) {
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            return Math.max(DEFAULT_CAPACITY, minCapacity);
        }
        return minCapacity;
    }

    private void ensureCapacityInternal(int minCapacity) {
        ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
    }

    private void ensureExplicitCapacity(int minCapacity) {
        modCount++;
        
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }

    private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

    private void grow(int minCapacity) {
        int oldCapacity = elementData.length;
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity;
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            newCapacity = hugeCapacity(minCapacity);
        elementData = Arrays.copyOf(elementData, newCapacity);
    }

这个扩容的过程有点复杂,于是我自己写了一个比较简单的扩容

public void judgeCapacity(int needCapacity) {
        if(needCapacity > elementData.length) {
            Object oldElementData[] = elementData;
            int newSize = this.size * 2 + 1;    // 扩容的空间
            elementData = new Object[newSize];
            elementData = Arrays.copyOf(oldElementData, newSize);   // 将之前的数组拷贝到新的数组中去
        }
    }

所谓扩容,就是判断需要的容量和给予的容量谁大谁小,如果需要的容量小,就不需要扩容,否则,就需要扩容。

当然,我的这个扩容非常的简单,肯定还是有不够完善的地方的,这也是我在写自己的 ArrayList 时用到的扩容方法,但是就目前来说并没有遇到什么问题。

然后就是其中的 clone( ) 方法,该方法返回此 ArrayList 实例的浅拷贝,元素本身不被复制。原码如下

public Object clone() {
        try {
            ArrayList<?> v = (ArrayList<?>) super.clone();
            v.elementData = Arrays.copyOf(elementData, size);
            v.modCount = 0;
            return v;
        } catch (CloneNotSupportedException e) {
            // this shouldn't happen, since we are Cloneable
            throw new InternalError(e);
        }
    }

在我的理解中,大致实现的过程就是先创建一个对象实例 v ,然后调用 v 中的 elementData 去拷贝原来的 elementData,最后再返回 v,这样就完成了元素的浅拷贝。

关于什么是浅拷贝、什么是深拷贝我也去做了一些了解。

我对于这两种拷贝方式的理解,浅拷贝就是设置一个新的对象,这个新的对象拷贝了旧的对象的内存地址,即两者是同一个对象,因此其中一个对象改变了这个地址,就会影响到另一个对象。示意图大致如下:

1650463872125.png

而深拷贝就是直接创建了一个新的对象,改变其中一个对象并不会影响到另一个对象。如图:

1650464096038.png

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值