一、什么是顺序存储和链式存储
1、 顺序存储方式其实就是存储的数据的内存地址是连续的,比如数组中的数据。
2、链式存储方式存储的数据的内存地址不一定是连续的,比如通过链表存储的数据。
二、顺序存储和链式存储的优缺点分析
1、顺序存储
比如顺序存储的数组中有6个数据,当删除第三个数据后,后面的数据必然会向前面移动;同理,当插入一个数据后,后面的数据也必然向后面移动。这样增删的效率显然是不高的,但是查询和修改数据是比较方便的,因为数据的内存地址是连续的,所以直接根据索引就能拿到相应的数据。
- 优点:查询和修改的效率高
- 缺点:增删效率不高
2、链式存储
比如链表中的数据其数据的内存地址不一定连续,是通过指针记录前一个或者后一个节点的内存地址而串联起来的。所以当要在原有的链条上增加或删除如一个节点就比较方便,只需要断开原有的链条然后再通过链条将节点串起即可。但是查询的效率比较第,因为每次都要从首节点或则尾节点通过遍历来查询到相应的节点。
- 优点:增删效率高
- 缺点: 查询效率低
所以在编程过程中我们需要根据实际的情况来选择数据结构,比如增删操作比较多就可以选择LinkedList
来存储数据,查询操作多就可以使用ArrayList
来存储数据。
三、ArrayList的分析
1、ArrayList
内部数据的存储方式
通过查看ArrayList
的源码,可以看到ArrayList
的内部是用一个Object[]
的数组来存放数据的,所以其内部数据的存储方式是顺序存储方式,其优缺点就很清楚了。
/**
* Default initial capacity.
*/
private static final int DEFAULT_CAPACITY = 10;
/**
* The array buffer into which the elements of the ArrayList are stored.
* The capacity of the ArrayList is the length of this array buffer. Any
* empty ArrayList with elementData == EMPTY_ELEMENTDATA will be expanded to
* DEFAULT_CAPACITY when the first element is added.
*
* Package private to allow access from java.util.Collections.
*/
transient Object[] elementData;
通过查看其成员变量就知道其默认的容量是10,那么问题来了,当我们存储的数据超过10个它又是如何扩容的呢?
2、ArrayList
的扩容
从源码中可以看到其扩容的核心方法就是grow(int minCapacity)
这个方法。
/**
* The maximum size of array to allocate.
* Some VMs reserve some header words in an array.
* Attempts to allocate larger arrays may result in
* OutOfMemoryError: Requested array size exceeds VM limit
*/
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
/**
* Increases the capacity to ensure that it can hold at least the
* number of elements specified by the minimum capacity argument.
*
* @param minCapacity the desired minimum capacity
*/
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;
}
从这句代码 int newCapacity = oldCapacity + (oldCapacity >> 1);
中可以看出ArrayList
扩容是增加原来容量的一半,如果还是不够那么新容量就等于所需要的容量(newCapacity = minCapacity
),但是容量最大只能为Integer.MAX_VALUE
这么大。在计算完所需要的容量后,ArrayList
是通过数组拷贝将原有数据拷贝到一个新数组中去来完成扩容的。在这句代码中就包含了新创建一个数组及拷贝的过程:elementData = Arrays.copyOf(elementData, newCapacity);
。
3、ArrayList
的增删改查操作
通过查看ArrayList
的源码知道,其内部也是通过Object[]
存储数据的,所以ArrayList
的增删改查操作都跟操作数组是一样的。增删的操作都会影响到后面的元素位置变化,所以起效率是不高的,而查询和修改的操作则比较方便,可以直接通过索引来操作元素。
本篇文章主要分析了顺序存储和链式存的优缺点,然后对顺序存储的ArrayList
做了简要的分析,了解了其内部数据是通过Object[]
来存储数据的,以及它是如何扩容等操作的。