Java集合底层原理
现在面试的时候相当多的面试官喜欢询问Java中集合的底层原理,因此整理后在这里对Java中集合的底层原理进行一下简单的介绍。
首先我们来看一下Java中集合的种类。
Java中集合主要有两个分类,单列集合和双列集合他们分别通过实现Collection和Map接口来实现,而Collection下又是由两个接口List和Set所继承。接下来我们就分别对其中的一些在Java中的底层实现原理做一下解析。
ArrayList集合的底层原理
首先来看一下ArrayList集合所实现的接口有哪些
其中所实现的RandomAccess接口让ArrayList能够快速随机访问,Serializable和Cloneable让ArrayList接口拥有序列化传输和克隆的能力。
1.ArrayList底层是由一个数组来实现的。通过它的一些成员变量就可以看出来。
private static final int DEFAULT_CAPACITY = 10;//创建ArrayList集合时,数组的默认长度
private static final Object[] EMPTY_ELEMENTDATA = {};//当我们创建ArrayList集合时指定其长度为0时,ArrayList就会使用这个数组来完成初始化
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};//当我们没有指定数组长度,类缺省选择的默认数组
transient Object[] elementData;//数组列表中存储元素的数组缓存区,数组列表的长度就是这个数组缓存区的长度,当带有elementData ==的数组添加第一个元素时,DEFAULTCAPACITY_EMPTY_ELEMENTDATA将会被扩展为DEFAULT_CAPACITY,因为带有transient关键字进行修饰,所以不会被序列化
private int size;//ArrayList集合的大小(其实是数组内含有的元素个数)
2.ArrayList的add()方法
它的源码如下
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
可以看出add()方法就是在数组元素的末尾添加新传入的值。但在之前首先调用了ensureCapacityInternal(size + 1),其实这个方法就是对要存储元素的数组大小进行判断,如果当前数组的大小小于(size+1),就对数组进行扩容,扩容后的数组大小时旧数组大小的1.5倍,然后利用Arrays.copyOf()方法,将旧数组的值复制到新数组中,这一段主要在ArrayList的grow()方法中实现。
3.ArraysList的set()方法
public E set(int index, E element) {
rangeCheck(index);
E oldValue = elementData(index);
elementData[index] = element;
return oldValue;
}
主体就是先取出旧元素,再把旧元素在数组的位置上赋上新元素的值,最后再返回旧元素,但在这之前还调用了rangeCheck(index)方法,这个方法其实就是对输入的index的值进行判断,如果index的值大于数组内元素,就抛出一个数组下标越界异常
4.ArrayList的remove()方法
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;
}
首先还是先对传入的index是否越界进行检查,根据index获取要存储的值并保存下来,然后再根据index判断移除的元素是否是最后一个元素,是的话只需要把这个元素的值赋值为null就可以了,否则就需要使用System.arraycopy(elementData, index+1, elementData, index,
numMoved)方法来使数组index+1后的元素都向前移动一位,最后返回刚才保存的被删除的元素就OK了
5.ArrayList的get()方法
public E get(int index) {
rangeCheck(index);
return elementData(index);
}
非常简单和上面的set()没有什么差别,就不解释了
6.ArrayList的indexOf()方法
public int indexOf(Object o) {
if (o == null) {
for (int i = 0; i < size; i++)
if (elementData[i]==null)
return i;
} else {
for (int i = 0; i < size; i++)
if (o.equals(elementData[i]))
return i;
}
return -1;
}
首先对传入的对象进行判断,如果是空值得话,就遍历数组(但仅仅遍历下标0到下标size-1的元素)返回数组中第一个为空值的index,否则就返回第一个与数组中元素相等的下标值。
7.ArrayList的优缺点
ArrayList的优点:
使用下标查询元素效率很高
凭借下标遍历元素效率高
自动扩容,每次扩容后的容量是原来的1.5倍,使用时不用考虑扩容问题
ArrayList的缺点:
插入和删除效率低
线程不安全
使用类似indexOf()的方式来查询集合内元素时效率低