ArrayList的底层原理
ArrayList的底层是一个动态的数组,数组的长度可以根据实际存储元素数量的增加而动态增长。
ArrayList的扩容机制:ArrayList的底层数组长度可以通过有参的构造方法给定,也可以通过无参的构造方法默认给定,默认长度是10,存储的元素会从第一个存储单元开始逐个存储,一旦存满,就会触发扩容机制。当需要扩容时,会先申明一个新的数组,它的长度是原数组长度的1.5倍,然后把原来数组中的元素逐一复制到新的数组中,然后把新数组作为ArrayList的底层数组(即更新引用),最后把要添加的那个元素添加到数组中。1.5倍的扩容可以减少空间的浪费,同时也足够承载新的元素。需要注意的是,ArrayList的查询的复杂度是确定的,但是添加元素的复杂度,还需要算上偶尔触发的扩容行为花费的时间复杂度,它是一个平均值。
ArrayList的优缺点分析:Arraylist的长度可以动态变化,在一定程度上弥补了数组的不足,但是每次触发扩容,都意味着要进行一次整个数组的复制,这会产生很大的时间复杂度。 ArrayList底层数组的实现保证了它很快的查找速度,但是在删除和添加元素操作时,效率比同为List集合下的LinkedList(双链表)低很多。
ArrayList的底层安全:ArrayList不是线程安全的,而Vector是线程安全的,Vector的底层方法多了synchronize关键字,确保在同一时间,只能有一个线程获得锁并访问这些方法;这两者的比较可以类似于HashMap和HashTable的比较。为什么我们还是会选择不是线程安全的ArrayList而越来越少使用Vector呢?因为ArrayList的效率很高,尤其体现在查询方面,支持了多线程的并发查询,效率是工程中永恒的话题。为了避免非线程安全的ArrayList在高并发下的一系列问题,我们可以通过在ArrayList的上层使用加锁来规范,比如在修改数据的时候确保只能有一个线程进行操作,而查询就不加限制,实现了高效率性,高灵活性,高安全性。
ArrayList的手写实现
接下来教大家如何手写实现一个简单的ArrayList:
实现的方法有:
int size();
MyArrayList();
MyArrayList(int initialCapacity);
boolean isEmpty();
Object get(int index);
boolean add(Object obj);
void add(int index,Object obj)
Object remove(int index)
boolean remove(Object obj)
Object set(int index,Object obj)
void rangeCheck(int index)
void ensureCapacity()
MyArrayList:
public class MyArrayList {
private int size;
private Object[] elementData;
/**
* 无参构造方法,
* 初始化size为0
* 初始化一个数组,大小为10
*/
public MyArrayList()
{
this.size=0;
this.elementData=new Object[10];
}
/**
* 有参构造方法
* 定义数组的初始化大小
* @param initialCapacity
*/
public MyArrayList(int initialCapacity)
{
if(initialCapacity<0)
{
try
{
throw new Exception();
}
catch(Exception e)
{
e.printStackTrace();
}
}
else
{
this.size=0;
this.elementData=new Object[initialCapacity];
}
}
/**
* 获取ArrayList元素数量
* @return
*/
public int size()
{
retu