Java 中 ArrayList 的实现解析

转载请注明出处:http://blog.csdn.net/hjf_huangjinfu/article/details/60607250


        ArrayList 是一个基于数组(顺序表)来实现 List 接口的类。下面记录一些在 ArrayList 在实现细节上可以学习的知识点。

 

增加/插入操作

该系列操作主要涉及的方法有:

public boolean add(E e)
public void add(int index, E element)
public boolean addAll(Collection<? extends E> c)
public boolean addAll(int index, Collection<? extends E> c)

 

因为是基于数组实现,该系列方法主要考虑的就是 扩容 的问题,只要容量没问题,按需要进行数据平移,然后按索引赋值就行了。下面看一下它内部的自动扩容逻辑。

 

    private void ensureCapacityInternal(int minCapacity) {
        if (elementData == EMPTY_ELEMENTDATA) {
            minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
        }

        ensureExplicitCapacity(minCapacity);
    }
    private void ensureExplicitCapacity(int minCapacity) {
        modCount++;

        // overflow-conscious code
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }
    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 void add() {
       //计算目前容量是否够用,不够用就扩容
        //计算最小容量需求
        if (列表为空) {
           最小容量 = 10(最低容量);
       } else {
           最小容量 = 当前容量 + 要添加的数据长度;
       }
       //扩容
        if (最小容量 > 当前容量) {
           //扩容策略
            新容量 = 当前容量 * 1.5;
            if (新容量 < 最小容量) {
                新容量 = 最小容量;
            }
            if (新容量 > Integer.MAX_VALUE) {
                新容量 = Integer.MAX_VALUE;
            }
            //复制数据
            创建长度为 ‘新容量’ 的数组,并且把原数组复制过来;
        }
        //正常插入数据
        正常插入数据,如果插入一个集合数据,采用数组赋值的方法来提高效率;
    }

这里扩容还用了一点小技巧,就是使用移位 num >> 1 来代替 num / 2

 

删除操作

该系列操作主要涉及的方法有:

public E remove(int index)
public boolean remove(Object o)
public boolean removeAll(Collection<?> c)
public boolean retainAll(Collection<?> c)
public void clear()

 

该系列方法中,单个删除没什么好说的,只是按索引删除,然后做数据平移就可以。主要是批量删除的方法,有一点小技巧,下面看一下:

 

    public boolean removeAll(Collection<?> c) {
        return batchRemove(c, false);
    }
    private boolean batchRemove(Collection<?> c, boolean complement) {
        final Object[] elementData = this.elementData;
        int r = 0, w = 0;
        boolean modified = false;
        try {
            for (; r < size; r++)
                if (c.contains(elementData[r]) == complement)
                    elementData[w++] = elementData[r];
        } finally {
            // Preserve behavioral compatibility with AbstractCollection,
            // even if c.contains() throws.
            if (r != size) {
                System.arraycopy(elementData, r,
                                 elementData, w,
                                 size - r);
                w += size - r;
            }
            if (w != size) {
                // clear to let GC do its work
                for (int i = w; i < size; i++)
                    elementData[i] = null;
                modCount += size - w;
                size = w;
                modified = true;
            }
        }
        return modified;
    }


批量删除的实现原理就是,基于双索引(读写索引)来操作数组,也没有依赖辅助的数组,遇到不需要 remove 的元素,就向写索引指向的位置写入数据。

 

伪代码如下:

    private void remove() {
        读索引 = 0;
        写索引 = 0;
        for (; 读索引 < size; 读索引++) {
            if (读索引指向的数据需要保留) {
                把 读索引指向的数据 写入到 写索引指向的位置;
                写索引++;
            }
        }
        把从 写索引 开始 到最后的所有数据置为null;
    }


 

下面示例演示了从 [A, B, C, D, E] 中 移除 [B, D] 的过程。

 

修改操作

该系列操作主要涉及的方法有:

public E set(int index, E element)

 

没什么好说的,就是检查索引边界,然后替换指定位置的数据。

 

查找操作

该系列操作主要涉及的方法有:

public E get(int index)
public int lastIndexOf(Object o)
public int indexOf(Object o)
public boolean contains(Object o)

没什么好说的,就是 按索引取值 或者  遍历匹配,只有在 lastIndexOf 中,换个方向遍历。

 

子列表

该系列操作主要涉及的方法有:

public List<E> subList(int fromIndex, int toIndex)

 

subList 为了减少内存使用的开销,内部原理是以offset 来控制 原来的List,所以对 sublist 修改,也会修改到原来的 list 。反之也成立

 

其他

数组中 size 以外的数据一律置为 null,防止内存溢出。

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值