帮你轻松搞定ArrayList问题!

1、简单说一下ArrayList用来干嘛的?

ArrayList是数组列表,主要用来装载数据, 当我们装载的是基本类型的数据int,long,boolean,short,byte…的时候我们只能存储他们对应的包装类,它的主要底层实现是数组 Object[] elementData

ArrayList 查询效率高,增删效率低,线程不安全。使用频率很高。

2、LinkedList、 Vector 与ArrayList

我们使用多的是查询,ArrayList查询效率高。

如果涉及频繁的增删可以使用LinkedList。

如果需要线程安全就使用Vector。

3、ArrayList扩容机制

数组的长度是有限制的,而ArrayList是可以存放任意数量对象,长度不受限制,那么他是怎么实现的呢?

其实实现方式比较简单,他就是通过数组扩容的方式去实现的。

看源码!

初始容量为10

//默认初始容量为10
private static final int DEFAULT_CAPACITY = 10;

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

可以看到扩容后的新容量=之前的容量+之前的容量右移1位(也就是除于2)

举个例子来看一下:

现在我们使用默认的初始容量为10,填满元素。
在这里插入图片描述
首先他会重新定义一个长度为10+10/2的数组也就是新增一个长度为15的数组。
在这里插入图片描述
然后把原数组的数据,原封不动的复制到新数组中,这个时候再把指向原数的地址换到新数组,ArrayList就这样完成了一次改头换面。
在这里插入图片描述
如果你传入了初始容量就用你传入的参数,如果没有,就使用默认的大小。

4、ArrayList怎么进行新增操作的

有两种方式进行新增操作。

第一种直接添加元素。

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

在新增之前有一步检验长度的判断ensureCapacityInternal,就是说如果长度不够,是需要扩容的,关于怎么扩容上面已将讲过。

第二种指定index新增。

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

在进行校验之后,及逆行了数组的copy,找一下arraycopy。

public static native void arraycopy(Object src,  int  srcPos,
                                        Object dest, int destPos,
                                        int length);

是一个native方法。

举个例子来理解一下这个方法:

比如有下面这样一个数组我需要在index 5的位置去新增一个元素A 。
在这里插入图片描述
那从代码里面我们可以看到,他复制了一个数组,是从index 5的位置开始的,然后把它放在了index 5+1的位置 。
在这里插入图片描述
给我们要新增的元素腾出了位置,然后在index的位置放入元素A就完成了新增的操作了 。

在这里插入图片描述

5、 ArrayList(int initialCapacity)会不会初始化数组大小?

不会初始化数组大小。

通过代码看一下:

import java.util.ArrayList;

public class Test {
    public static void main(String[] args) {
        ArrayList<Integer> arrayList = new ArrayList(10);
        System.out.println(arrayList.size());
        arrayList.set(5,1);
    }
}

输出结果:报错了。
在这里插入图片描述

6、ArrayList怎么进行删除操作的?

看源码:

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

从源码中我们可以看到还是arraycopy。

还是举个例子来理解:

我们现在要删除index 为5的这个位置。
在这里插入图片描述
那代码他就复制一个index5+1开始到最后的数组,然后把它放到index开始的位置
在这里插入图片描述

index5的位置就成功被”删除“了其实就是被覆盖了,给了你被删除的感觉。

同理他的效率也低,因为数组如果很大的话,一样需要复制和移动的位置就大了。

发布了316 篇原创文章 · 获赞 39 · 访问量 3万+

猜你喜欢

转载自blog.csdn.net/hello_cmy/article/details/105510561