1. 底层数据结构
-
ArrayList是List接口的可调整大小的数组实现
-
增删慢:每次增删数据,需要拷贝以及移动元素位置
-
查询快:数组内存是一块连续空间,可以根据索引方式获取对应位置元素
2. 实现与继承
2.1 Serializable接口
序列化接口,没有方法或字段,仅用于标识,实现该接口才能实现序列化反序列化
-
序列化:将对象的数据转换成字节序列写入文件
-
反序列化:将文件中的字节序列数据恢复出来
-
序列版本号:
-
序列版本号是序列化前后唯一标识符号
-
如果没有人为指定序列版本号,编译器会自动添加
-
反序列化时,JVM会把字节流中的序列版本号与被序列化类中的序列版本号进行比对,一致才会进行反序列化,否则报错
-
建议实现该接口后人为写序列版本号(或自动添加)
-
static修饰部分不会被序列化:因为序列化保存的是对象的状态,与类无关
-
transient修饰部分不会被序列化:希望某些字段不被序列化(如密码),该字段反序列化结果为null
-
2.2 Cloneable接口
克隆接口
-
被克隆对象的类必须实现克隆接口
-
重写clone方法
-
浅拷贝通过重写clone方法实现
-
深拷贝推荐使用序列化方式实现
2.3 RandomAccess接口
快速随机访问接口,允许算法更改其行为,以便应用于随机访问列表或顺序访问列表时提供良好的性能
-
实现了该接口的List,则使用普通for循环随机访问,ArrayList
-
没有实现该接口的List,则使用增强for循环或迭代器进行顺序访问,LinkedList
2.4 AbstractList抽象类
List接口的骨架实现,ArrayList,LinkedList都是其子类
3. 源码分析
3.1 构造方法
-
ArrayList():构造初始容量为10的空列表
-
ArrayList(Collection<? extends E> c):构造一个包含指定集合元素的列表,按照他们由集合的迭代器返回的顺序
-
ArrayList(int initialCapacity):构造具有指定初始容量的空列表
3.2 添加方法
-
public boolean add(E, e):指定元素添加到列表末尾
-
扩容:扩容为原来的1.5倍
-
-
public void add(int index, E, element):指定位置添加元素
-
指定位置添加:指定位置后元素先后移,指定位置再添加元素
-
-
public boolean addAll(Collection<? extends E> c):c中元素追加到列表末尾,按照c迭代器返回顺序添加
-
public boolean addAll(int index, Collection<? extends E> c):c中元素添加到指定位置
3.3 迭代器
public Iterator<E> iterator():以正确顺序返回该列表中元素的迭代器
集合的迭代器接口public interface Iterator<E>
方法 | 说明 |
---|---|
default void forEachRemaining(Consumer<? super E> action) | 对每个元素进行某种操作 |
boolean hasNext() | |
E next() | |
default void remove() | 从集合中删除此迭代器返回的最后一个元素 |
补充:
可迭代接口public interface Iterable<E>,实现了该接口的对象可以使用增强for循环
方法 | 说明 |
---|---|
default void forEach(Consumer <? super T> action) | 对每个元素执行操作 |
Iterator<T> iterator() | 返回T元素的迭代器 |
3.4 其他方法
略
4. 常见面试题
4.1 如何扩容
构建新数组,新数组大小为原数组的1.5倍,原数组元素复制进入新数组
4.2 频繁扩容导致添加性能急剧下降,如何解决
通过指定初始容量初始化数组
4.3 插入删除元素一定慢吗
取决于元素离数组末端距离,不一定每次插入都需要构建新数组,因此不一定比LinkedList慢
4.4 线程安全问题
ArrayList非线程安全,线程安全版本为Vector.
4.5 和LInkedList区别
问:ArrayList,LinkedList哪个更占空间?
答:
-
表面上LInkedList更占空间(包括前索引,后索引)
-
实际上如果ArrayList添加元素超过数组大小时,需要扩容为1.5倍,如果只添加一个元素则会有较大的空间浪费,因此ArrayList不一定比LinkedList节省空间.
-
ArrayList中elementData表示底层数组,默认10,用transient修饰,size表示数组中元素个数.ArrayList重写了writeObject方法,序列化时候不会把elementData数组完全序列化,只序列化elementData中的前size个元素,空值不会序列化.
2. 实现与继承
2.1 Serializable接口
序列化接口,没有方法或字段,仅用于标识,实现该接口才能实现序列化反序列化
- 序列化:将对象的数据转换成字节序列写入文件
- 反序列化:将文件中的字节序列数据恢复出来
- 序列版本号:
- 序列版本号是序列化前后唯一标识符号
- 如果没有人为指定序列版本号,编译器会自动添加
- 反序列化时,JVM会把字节流中的序列版本号与被序列化类中的序列版本号进行比对,一致才会进行反序列化,否则报错
- 建议实现该接口后人为写序列版本号(或自动添加)
- static修饰部分不会被序列化:因为序列化保存的是对象的状态,与类无关
- transient修饰部分不会被序列化:希望某些字段不被序列化(如密码),该字段反序列化结果为null
2.2 Cloneable接口
克隆接口
- 被克隆对象的类必须实现克隆接口
- 重写clone方法
- 浅拷贝通过重写clone方法实现
- 深拷贝推荐使用序列化方式实现
2.3 RandomAccess接口
快速随机访问接口,允许算法更改其行为,以便应用于随机访问列表或顺序访问列表时提供良好的性能
- 实现了该接口的List,则使用普通for循环随机访问,ArrayList
- 没有实现该接口的List,则使用增强for循环或迭代器进行顺序访问,LinkedList
2.4 AbstractList抽象类
List接口的骨架实现,ArrayList,LinkedList都是其子类
3. 源码分析
3.1 构造方法
- ArrayList():构造初始容量为10的空列表
- ArrayList(Collection<? extends E> c):构造一个包含指定集合元素的列表,按照他们由集合的迭代器返回的顺序
- ArrayList(int initialCapacity):构造具有指定初始容量的空列表
3.2 添加方法
-
public boolean add(E, e):指定元素添加到列表末尾
- 扩容:扩容为原来的1.5倍
-
public void add(int index, E, element):指定位置添加元素
- 指定位置添加:指定位置后元素先后移,指定位置再添加元素
-
public boolean addAll(Collection<? extends E> c):c中元素追加到列表末尾,按照c迭代器返回顺序添加
-
public boolean addAll(int index, Collection<? extends E> c):c中元素添加到指定位置
3.3 迭代器
public Iterator
集合的迭代器接口public interface Iterator
方法 | 说明 |
---|---|
default void forEachRemaining(Consumer<? super E> action) | 对每个元素进行某种操作 |
boolean hasNext() | |
E next() | |
default void remove() | 从集合中删除此迭代器返回的最后一个元素 |
补充:
可迭代接口public interface Iterable
方法 | 说明 |
---|---|
default void forEach(Consumer <? super T> action) | 对每个元素执行操作 |
Iterator |
返回T元素的迭代器 |
3.4 其他方法
略
4. 常见面试题
4.1 如何扩容
构建新数组,新数组大小为原数组的1.5倍,原数组元素复制进入新数组
4.2 频繁扩容导致添加性能急剧下降,如何解决
通过指定初始容量初始化数组
4.3 插入删除元素一定慢吗
取决于元素离数组末端距离,不一定每次插入都需要构建新数组,因此不一定比LinkedList慢
4.4 线程安全问题
ArrayList非线程安全,线程安全版本为Vector.
4.5 和LInkedList区别
问:ArrayList,LinkedList哪个更占空间?
答:
- 表面上LinkedList更占空间(包括前索引,后索引)
- 实际上如果ArrayList添加元素超过数组大小时,需要扩容为1.5倍,如果只添加一个元素则会有较大的空间浪费,因此ArrayList不一定比LinkedList节省空间.
- ArrayList中elementData表示底层数组,默认10,用transient修饰,size表示数组中元素个数.ArrayList重写了writeObject方法,序列化时候不会把elementData数组完全序列化,只序列化elementData中的前size个元素,空值不会序列化.
4.6 如何复制一个ArrayList到另一个ArrayList
- clone()方法
- ArrayList构造方法
- addAll方法-p">
4.6 如何复制一个ArrayList到另一个ArrayList
-
clone()方法
-
ArrayList构造方法
-
addAll方法