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()
{
return size;
}
/**
* 判断当前ArrayList是否为空
* @return
*/
public boolean isEmpty()
{
return size==0;
}
/**
* 查询指定下标的元素对象
* @param index
* @return
*/
public Object getIndex(int index)
{
if(index>=0&&index<size)
return elementData[index];
else
{
try
{
throw new ArrayIndexOutOfBoundsException();
}
catch(ArrayIndexOutOfBoundsException e)
{
e.printStackTrace();
}
return null;
}
}
/**
* 队尾添加元素
* 1、先对容量进行确认,如果满了需要扩容
* 2、添加一个元素到队尾
* @param obj
* @return
*/
public boolean add(Object obj)
{
ensureCapacity();
elementData[size]=obj;
size++;
return true;
}
/**
* 指定下标添加
* 1、下标检查
* 2、数组容量检查
* 3、后移部分数据
* 4、插入数据
* @param index
* @param obj
*/
public void add(int index , Object obj)
{
rangeCheck(index);
ensureCapacity();
System.arraycopy(elementData, index, elementData, index+1, size-index);
elementData[index]=obj;
size++;
}
/**
* 删除指定下标的元素
* 1、下标检查
* 2、保存要删除的元素
* 3、部分前移覆盖
* 4、返回被删除的元素
* @param index
* @return
*/
public Object remove(int index)
{
rangeCheck(index);
Object obj = elementData[index];
System.arraycopy(elementData, index+1, elementData, index, size-index-1);
size--;
return obj;
}
/**
* 删除指定元素
* 1、元素的位置
* 2、删除元素并返回true
* 3、查找失败返回false
* @param obj
* @return
*/
public boolean remove(Object obj)
{
for(int i=0;i<size;i++)
{
if(elementData[i].equals(obj))
{
System.arraycopy(elementData, i+1, elementData, i, size-i-1);
size--;
return true;
}
}
return false;
}
/**
* 设置指定下标的元素内容
* @param index
* @param obj
* @return
*/
public Object set(int index,Object obj)
{
rangeCheck(index);
Object objReturn = elementData[index];
elementData[index]=obj;
return objReturn;
}
/**
* 下标检查
*/
private void rangeCheck(int index) {
// TODO Auto-generated method stub
if(index<0||index>=size)
{
try
{
throw new ArrayIndexOutOfBoundsException();
}
catch(ArrayIndexOutOfBoundsException e)
{
e.printStackTrace();
}
}
}
/**
* 数组扩容判断方法
*/
private void ensureCapacity() {
// TODO Auto-generated method stub
if(size==elementData.length)
{
System.out.println("发生了扩容");
Object[] elementData = new Object[(int)((double)this.elementData.length*1.5)];
for(int i=0;i<size;i++) //把之前数组的值都复制到新的数组
{
elementData[i] = this.elementData[i];
}
this.elementData=elementData; //把新的数组定义为当前数组
}
}
public static void main(String[] args)
{
MyArrayList ma = new MyArrayList();
ma.add(1);
ma.add("ddd");
ma.add(4);
int[] a =new int[100]; //创建一个对象的引用,用来作为一个测试元素
ma.add(a);
ma.add("sssss");
ma.add("sd");
ma.add("3445");
ma.add("555");
ma.add("736");
ma.add(55);
ma.add("nn");
ma.add("456");
ma.add("tt");
ma.add(57);
ma.add("ok");
System.out.println("是否为空:"+ma.isEmpty());
System.out.println("队尾添加元素");
for(int i=0;i<ma.size;i++)
System.out.println(ma.getIndex(i));
ma.add(3, "123");
System.out.println("在指定index=3位置添加元素123");
for(int i=0;i<ma.size;i++)
System.out.println(ma.getIndex(i));
ma.remove(2);
System.out.println("删除index=2位置的元素");
for(int i=0;i<ma.size;i++)
System.out.println(ma.getIndex(i));
ma.remove(a);
System.out.println("删除a对象");
for(int i=0;i<ma.size;i++)
System.out.println(ma.getIndex(i));
ma.set(11, "888");
System.out.println("设置index=11处字符串为888");
for(int i=0;i<ma.size;i++)
System.out.println(ma.getIndex(i));
ma.remove(12);
System.out.println("删除index=12的元素");
for(int i=0;i<ma.size;i++)
System.out.println(ma.getIndex(i));
}
}
测试结果(测试所有方法,并使得元素数量超过10,测试扩容机制):
发生了扩容
是否为空:false
队尾添加元素
1
ddd
4
[I@15db9742
sssss
sd
3445
555
736
55
nn
456
tt
57
ok
发生了扩容
在指定index=3位置添加元素123
1
ddd
4
123
[I@15db9742
sssss
sd
3445
555
736
55
nn
456
tt
57
ok
删除index=2位置的元素
1
ddd
123
[I@15db9742
sssss
sd
3445
555
736
55
nn
456
tt
57
ok
删除a对象
1
ddd
123
sssss
sd
3445
555
736
55
nn
456
tt
57
ok
设置index=11处字符串为888
1
ddd
123
sssss
sd
3445
555
736
55
nn
456
888
57
ok
删除index=12的元素
1
ddd
123
sssss
sd
3445
555
736
55
nn
456
888
ok