Java知识:ArrayList类详解

来源:互联网 发布:底盘装甲 知乎 编辑:程序博客网 时间:2024/06/15 19:46

1、ArrayList的本质是什么?

ArrayList本质是顺序表!

2、 什么是顺序表?

顺序表是在计算机内存中以数组的形式保存的线性表,是指用一组地址连续的存储单元依次存储数据元素的线性结构。只要确定了起始位置,表中任一元素的地址都通过下列公式得到:LOC(ai)=LOC(a1)+(i-1)*L  1≤i≤n 其中,L是元素占用存储单元的长度。

这里写图片描述

简单点说就是:以数组的形式保存的线性表(属于线性表中的一种)

3、ArrayList这个顺序表是如何实现的呢,证据呢?

ArrayList是通过数组实现的。ArrayList构造函数如下:

一下代码摘抄自JDK源码,我只摘抄了需要用到的部分。

private transient Object[] elementData;//声明了一个object类型的数组public ArrayList(int initialCapacity) {        super();        if (initialCapacity < 0)            throw new IllegalArgumentException("Illegal Capacity: "+                                               initialCapacity);        this.elementData = new Object[initialCapacity];//创建了一个initialCapacity容量的数组    }    /**     * Constructs an empty list with an initial capacity of ten.     */    public ArrayList() {        this(10);    }
在构造函数中有一句话this.elementData = new Object[initialCapacity];//创建了一个initialCapacity容量的数组

4、ArrayList初始容量是多少呢?

根据构造函数如果我们自定义了容量:ArrayList<String> al=new ArrayList<String>(20);那么容量就是20如果我们没有自定义容量:ArrayList<String> al=new ArrayList<String>();那么容量就是10

5、ArrayList扩容扩多少呢?

现在我们看看add操作
public boolean add(E e) {        ensureCapacityInternal(size + 1);  // Increments modCount!!        elementData[size++] = e;        return true;    }

add操作的第一步就是执行ensureCapacityInternal方法。

private void ensureCapacityInternal(int minCapacity) {        modCount++;        // overflow-conscious code        if (minCapacity - elementData.length > 0)            grow(minCapacity);    }

modCount++;这个一会儿会讲到
if (minCapacity - elementData.length > 0)
grow(minCapacity);//如果容量小了就执行grow(minCapacity)函数,看来grow(minCapacity)是扩容函数的庐山真面目啊

private void grow(int minCapacity) {        // overflow-conscious code        int oldCapacity = elementData.length;        int newCapacity = oldCapacity + (oldCapacity >> 1);//扩容50%        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);    }

newCapacity = oldCapacity + oldCapacity / 2;
就是说每次当发现当前数组长度不足时,每次增加的步长是【0.5倍的当前长度】。
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
比方说你有个10容量的数组,现在要通过addAll()方法增加10个元素,那么minCapacity就是20,newCapacity就是15,15小于20那么newCacacity就变成了20.也就是说此次扩容了100%。

if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
如果你的newCapacity比MAX_ARRAY_SIZE(Integer.MAX_VALUE - 8)还要大的话就要看看 hugeCapacity(int minCapacity) 方法了;

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

根据hugeCapacity(int minCapacity) 方法,newCapacity= Integer.MAX_VALUE
这次扩容的比例应该小于等于50%

网上有人说他每次扩容50%也是不准确的。

6、在ArrayList中我们经常看到modCount,这个变量有什么用呢?

这位大神给出了很好的解答:
http://blog.csdn.net/qq_24235325/article/details/52450331
这其实Fail-Fast机制快速失败机制,这个机制以后再发博客吧。

7、protected transient int modCount = 0;中的transient关键字有啥用呢?

transient 修饰的变量不会序列化,关于序列化的知识以后再详细说明吧。

8、ArrayList是基于动态数组实现的?

Java中的数组都是静态数组(数组大小不可变)。
那ArrayList是基于动态数组实现的是不正确的吗?
ArrayList实现动态数组本质的创建新的数组,只是看起来原来的数组动态增长了!
这句话的正确与否取决于你对动态数组的理解和认识,你认为之前有个数组,之后创建一个新的可以算作“动态”那这句话是正确的。
如果你认为创建了一个新的,原来的是”静态“的,那么这句话是不正确的。
这个问题没有必要较真。

9、ArrayList为什么是不同步的?

看看jdk里ArrayList的add方法是如何写的

这里写图片描述
问题就出在size++这边,主要分为两个步骤:

  1)将add的元素放到size位置  2)将size加1

  假设size=5.若线程A在5位置存放了值valueA,获得size=5,但还没来得及将size加1写入主存。此时线程B在也在5位置存放了值valueB,也获得size=5,而后A、B分别将size加1后写入主存,size=6,即两个线程执行两次add()后size只加了1。

总结:

1、ArrayList适合查找,不适合频繁的增删。
2、ArrayList不同步
3、如果可以确定ArrayList所需容量最好直接定义。

参考资料:
http://blog.csdn.net/li_ning_/article/details/52089861
http://www.cnblogs.com/dick159/p/6531004.html
http://www.cnblogs.com/shamo89/p/6672104.html
http://blog.csdn.net/qq_24235325/article/details/52450331

原创粉丝点击