ArrayList详解(源码讲解)

ArrayList继承那些类,实现那些接口

我们可以通过源码看到 ArrayList 继承AbstractList 类实现了List、RandomAccess、Cloneable,java.io.Serializable 4个接口
实现了List接口,可随机访问,可克隆,可序列化。

List相对于Collection多了那些方法?

List,是一个不允许null,有序,允许重复元素的接口。
在这里插入图片描述

方法描述
E get(int)根据 int 下标获取元素
E set(int , E)设置 int 下标的元素
Void add(int , E)在 int 位置下添加对象 E
E remove(int )删除 int 下标的元素
int indexOf( Object )获得在 List 中第一个此对象的下标位置
int lastIndexOf( Object )获得在 List 中最后一个此对象的下标位置
Listiterator listIterator()返回ListIterator迭代器
ListIterator listIterator(int)从指定位置开始,这是ListIterator next()方法的第一个值
List subList(int fromIndex,int toIndex)返回此列表中指定的 fromIndex (含)和 toIndex之间的视图。
Spliterator spliterator()在此列表中的元素上创建一个Spliterator

允许null么?有序么?允许重复么?

允许为空,让我们看一下add()方法的源码,在这里插入图片描述
并没有判断这个对象是否为空就直接插入了。

有序,插入的方法,默认是尾部添加。
允许重复,再插入的时候,并没有检索数组中的元素,判断是否有重复。

ArrayList底层实现以及常用方法

ArrayList的底层的数据结构是基于数组实现的,并且默认初始大小为
10

构造器
ArrayList()
直接将内部的空数组成员变量赋给容器,在第一次add数据的时候初始化数组,容量为10
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
ArrayList(int initialCapacity)
以传入的initialCapacity作为初始化大小,初始化容量如果为0,直接将空数组赋给elements,如果初始容量小于0,直接抛出异常。
在这里插入图片描述

ArrayList(Collection<? extends E> c)
构造一个包含指定集合的元素的列表,按照它们由集合的迭代器返回的顺序。
如果使用这种方式,那么在底层ArrayList会直接将 c 集合先转成数组,赋给elementData,然后对长度进行判断,如果为0,那么直接将空数组赋给elementData。
在这里插入图片描述

扩容:1.5倍扩容,并且容器的最大值为 Integer.MAX_VALUE - 8
在这里minCapacity指的是size+1,我们可以发现newCapacity也就是我们的新容量变成了1.5倍。
如果newCapacity减去minCapacity <0也就是说我新的容量不满足现在的最小容量,那么就直接把minCapacity赋给newCapacity。
并且还要判断这个新的容量是是否大于容器容量的最大值,如果大于了,那么就把最大值赋给newCapacity。
在这里插入图片描述
增:
boolean add(E e)
先检查容量是否需要扩容,再将 e 插入到尾部,返回true。
在这里插入图片描述

void add(int index , E e)
先调用RangeCheckForAdd(index)检查下标合法性,不合法直接抛出下标越界异常。
检查+1后的容量是否需要扩容,使用System.arraycopy()方法进行移动,再将 e 放置到index下标
(tips:System,arraycopy(Object src, int srcPos, Object dest, int destPos, int length); src为源数组,srcPos为复制的开始下标,dest为目标数组,destPos从这个下标开始粘贴,int length为复制的长度。)
在这里插入图片描述

删:
E remove(int index) 删除指定下标的元素
ModCount++,先通过elementData(index)获取到此下标的元素并保存,再通过System.arraycopy()去调整数组。–size = null,之后返回删除的元素。
在这里插入图片描述
public boolean remove(Object o) 删除指定元素
遍历数组,如果找到该元素直接调用FastRemove(index)。删除成功返回true;
在这里插入图片描述
private void fastRemove(int index) 快速删除指定下标的元素
(这个私有删除方法直接跳过了容量检查,并且不返回原有元素) ModCount++, 使用System.arraycopy()调整数组。
在这里插入图片描述

改:
public E set(int index, E element)改动指定位置的元素,返回原有元素
先调用rangeCheck(index)检查下标,如果下标大于了size 直接抛出下标越界异常。
调用ElementData(index)保存原有元素,将 element 放置到 index 下标,返回原有元素。
在这里插入图片描述
查:
public int indexOf(Object o),从前向后查询在数组中第一个 o 对象所对应的下标
找不到,则返回-1。
在这里插入图片描述
public int lastIndexOf(Object o) 从后向前查询,找到数组中第一个 o 对象所对应的下标
找不到,则返回-1
在这里插入图片描述
E elementData(int index) 直接返回数组所对应下标的元素

在这里插入图片描述
public boolean contains(Object o)
查询在ArrayList中是否有这个对象,有则返回true。
底层直接调用了indexOf(o)方法获取索引,,只要索引下标大于等于0就证明存在
在这里插入图片描述

快速失败机制

我们会发现在这些方法中,都会有 ModCount++的操作,这个是为什么呢?
为什么要对ModCount++进行操作呢

我们来看一下那些方法进行了ModCount++操作。

ensureExplicitCapacity(),判断是否需要扩容,
remove(int index) ,删除指定下标位置
remove(Object o)和fastRemove(int index)(remove 调用fastRemove所以两个放在一起),删除指定元素。
clear(),清空数组;
void removeRange(int fromIndex, int toIndex),删除从fromindex到toIndex的元素。

那么还有一个方法就是 迭代器 Iterator
首先简单介绍一下迭代器接口中的最主要的三个方法。
next(); 返回下一个元素
hasnext(); 是否有下一个元素?
remove();从基础集合中移除此迭代器返回的最后一个元素。

在ArryaList的迭代器中有两个变量
游标cursor,这个变量存放下一个元素,也就是next()方法的元素下标
最后返回的数据的下标lastRet = -1 ,从-1开始。

我们简单来说ArrayList的快速失败机制
fail-fast 机制是Java集合(Collection)中的一种错误机制。当多个线程对同一个集合的内容进行操作时,就可能会产生fail-fast(快速失败)事件。为什么,假如两个线程同时对ArrayList进行操作,那么就很有可能导致ArrayList数据出错,当删除的时候我们相当于改变了这个ArrayList的结构,所以我们采用fail-fast机制来解决这样的问题。
当ArrayList 使用 Iterator去遍历数组时,会将ModCount传进去,这个参数变成了exceptedModCount,当我们遍历的时候就会检查当前的ModCount和execeptedModCount是否一致,如果不一致直接抛出异常,因为在遍历时,遍历的文件的结构发生了改变,很有可能导致遍历出错。
但是,无论fail-fast机制再怎么完善,ArrayList依旧是非线程安全的集合

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值