ArrayList数组

 一直想好好介绍下ArrayList数组,今天外面下雨,不能出去浪,今天正好在家好好总结下ArrayList,Java基础面试基本会闻到集合,Collecton类,以及各个实现类的特点,今天就从源码的角度分析下ArrayList数组,后面更新面试问的最多的HashMap。

好了,废话不多说,开始......,请开始你的表演!!!!

什么是ArrayList

        ArrayList就是数组列表,主要是用来装载数据,能用来装int,long,boolean,short,byte类型,我们只能存储他们对应的包装类,它的底层实现是数组Object[]elementData。

        类似的有LinkedList,和LinkedList相比,它的查找和访问元素的速度比较快,但是新增,删除的速度较慢。 (至于为什么慢,后面单独介绍下LinkedList)。

总结:ArrayList底层是用数组实现的。

特点:查询效率高,增删效率低,线程不安全,使用频率很高。

第一个问题来了,为啥它是线程不安全的,我们在日常的开发中还会用到呢?

        因为我们平时使用的场景都是用来做查询,不会涉及到太频繁的增加,删除操作。如果涉及到频繁的增加,删除操作,那么我们可以用LinkedList,如果你需要用线程安全的,可以使用Vector(但是它是重量级的)。在实际的开发中,还是ArrayList使用最多的。

下面来说说底层的数组,以及数组的大小,如果一直不断的往数组里添加数据的话,会不会出现问题?

        通过无参构造的方式ArrayList()初始化,则赋值底层Object[] elementData为一个默认空数组Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {}所以数组容量为0,只有对真正数据添加,也就是add的时候,才分配默认DEFAULT_CAPACITY=10的初始容量。

那么有个问题,数组的长度是有限制的,而ArrayList是可以存放任意数量对象,长度不受限制,那么它是怎么实现的呢?

其实实现很简单,通过数组的扩容去实现的。

比如我们现在有一个长度为10的数组,现在要新增一个元素,发现数组满了,那ArrayList会怎么做呢?

首先,它会从新定义一个长度为10+10/2的数组,也就是新增一个长度为15的数组。然后把所数据原封不动的放进去,这个时候在把指向原数组的地址换到新数组,就OK了,是不是很简单!!!

为什么我要用长度为10的数组来举例子呢,看到这,如果有小伙伴读过源码的会知道我的用意。因为,我们在使用ArrayList的时候一般不会设置初始大小,那ArrayList默认的大小就刚好是10,是不是很有意思,哈哈 !!!!

看源码的时候,我们可以看到,它的构造方法,如果你传入了初始值大小,那就用你传入的参数,如果没有,那就使用默认的,这一切都是老天安排好的, 哈哈 哈哈 哈哈!!!!

当然版本的不同,源码的方式也不同。就好比在1.7版本以前调用this(10)才是真正容量为10。1.7以后默认走空数组,只有第一次add的时候容量才会变成10。

那么有人问了,为啥默认是10,不是8,14呢?

据说是Sun公司程序员对一系列广泛使用的程序代码进行了调研,结果10这个长度的数组是最常用最效率的。换一种方式说,就是随便起的一个数字,8,14个都没有区别,可能10这个数字比较吉祥吧,哈哈哈哈哈哈!!!!!

下面来说说,它增加,删除怎么做的吧,结合源码来看。

他有指定index新增,也有直接新增的,在这之前它会有一步校验长度的判断,也就是ensureCapacityInternal,如果长度不够,就要进行扩容。(在扩容的时候,老版本的jdk和8以后的版本是有区别的,8之后的效率高,采用了位运算,右移一位,也就是除以2的操作。1.7的时候3/2+1,1.8直接就是3/2)

指定位置新增的时候,在校验之后的操作很简单,就是数组的Copy,看下源码。

比如,下面有这样一个数组我需要在index5的位置去新增一个元素J。

 

 从源码里面可以看到,他复制了一个数组,是从index5的位置开始的,然后把它放在index5+1的位置。

给要添加的元素腾出了位置,然后在index的位置上放入元素J就完成了新增的操作了。

为啥说它的效率低,我想我不用说,小伙伴们都知道了,这只是一个小的List,如果要是成百上千的List去新增一个元素,那就需要后面所有的元素都要移动,如果在涉及到扩容那更慢了,哈哈 !

问题来了,ArrayList会不会初始化数组大小?

思考了一会......,当然会初始化大小了,但是List的大小没有变,因为List的大小是返回size的,来我们试试。

 小伙伴们发现虽然我对ArrayList设置了初始大小,但是打印出来是空,在操作下标进行set值的时候,数组下标越界了。

其实数组是初始化了,但是List没有,那size就没有变,set下标和size下标比较那就报错了。

那么ArrayList删除元素会慢吗?

 这就取决你删除的元素离数组末端有多远,ArrayList拿来用作堆栈是比较好的,push和pop操作完不涉及数组的移动。

ArrayList怎么删除元素?

 其实删除跟新增是一样的。看源码还是copy一个新数组。

在用上面的例子来实现删除

 

那么 它复制一个index5+1开始到最后的数组,然后把它放到index开始的位置。

index5的位置就成功的被“删除”了,其实就是被覆盖了,给人一种被删除的感觉。

那么ArrayList适合用来做队列吗?

队列一般是FIFO(先进先出),如果用ArrayList做队列,就需要在数组尾部追加数据,数组的头部删除数据,但是无论无何都会涉及到数组数据搬迁,这个比较消耗性能的。

结论:ArrayList不适合做队列。

AttayList常用的方法总结

boolean add(E e) 将指定的元素添加到此列表的尾部。

void add(int index, E element) 将指定的元素插⼊此列表中的指定位置。

boolean addAll(Collection c) 按照指定 collection 的迭代器所返回的元素顺序,将该 collection 中的所有元素添加到此列表的尾部。

boolean addAll(int index, Collection c) 从指定的位置开始,将指定 collection 中的所有元素插⼊到此列表中。

void clear() 移除此列表中的所有元素。

Object clone() 返回此 ArrayList 实例的浅表副本。

boolean contains(Object o) 如果此列表中包含指定的元素,则返回 true。 void ensureCapacity(int minCapacity) 如有必要,增加此 ArrayList 实例的容量,以确保它⾄少能够容纳最⼩容量参数所指定的元素数。

E get(int index) 返回此列表中指定位置上的元素。

int indexOf(Object o) 返回此列表中⾸次出现的指定元素的索引,或如果此列表不包含元素,则返回 -1。

boolean isEmpty() 如果此列表中没有元素,则返回 true int lastIndexOf(Object o) 返回此列表中最后⼀次出现的指定元素的索引,或如果此列表不包含索引,则返回 -1。

E remove(int index) 移除此列表中指定位置上的元素。

boolean remove(Object o) 移除此列表中⾸次出现的指定元素(如果存在)。

protected void removeRange(int fromIndex, int toIndex) 移除列表中索引在 fromIndex(包括)和 toIndex(不包括)之间的所有元素。

E set(int index, E element) ⽤指定的元素替代此列表中指定位置上的元素。

int size() 返回此列表中的元素数。

Object[] toArray() 按适当顺序(从第⼀个到最后⼀个元素)返回包含此列表中所有元素的数组。 T[] toArray(T[] a) 按适当顺序(从第⼀个到最后⼀个元素)返回包含此列表中所有元素的数组;返回数组的运⾏时类型是 指定数组的运⾏时类型。

void trimToSize() 将此 ArrayList 实例的容量调整为列表的当前⼤⼩。

好了,ArrayList就总结到这,下篇文章介绍著名的HashMap!

 

 

 

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值