ArrayList 你了解这些吗?
本文参考自:
ArrayList是如何扩容的?
初始容量为 10,第一扩容就为 10。
以后每次的扩容都是原容量的 1.5 倍。
ArrayList继承关系
1 | |
实现Serializable标记型接口
序列化:将对象转化为字节数组的过程
反序列化:将字节数组转化为对象的过程
实现Cloneable标记型接口
克隆:将ArrayList集合的数组clone到另一个集合
实际底层调用的是Object中的clone方法,而clone是一个native方法
1 | |
使用了Arrays#copyOf方法进行复制
1 | |
浅拷贝:基本数据类型可以达到完全复制,引用数据类型拷贝的是栈上的地址值,所以修改引用类型的数据,会改变原来的数据。
深拷贝:基本数据类型和引用类型都可以完全复制,引用对象在堆中创建新的对象,对原数据没有任何影响。
深拷贝的实现方式:
实现Cloneable接口并重写clone方法。
让其clone方法通过序列化和反序列化的方式来生成一个原对象的深拷贝副本
实现RandomAccess标记型接口
ArrayList支持随机访问。
随机访问:直接
first+N,便可以得到第N个元素的地址,因为这些相邻元素是按顺序连续存储的。
比如普通数组就是可随机访问的。文件随机访问是指在某个文件内直接读写任何给定位置数据的能力。
通过get(i)即可获得相应内存中存放的值。原因是因为ArrayList存放的内容在内存中是连续的,数组直接用[]访问,相当于直接操作内存地址,所以随机访问的效率较高。
普通的for循环是随机访问的,所以遍历ArrayList使用普通for循环比增强for循环和迭代器的效率高。
而LinkedList是一个双向链表,链表只能顺序访问,不支持随机访问,LinkedList中的get方法是按照顺序从列表的一端开始检查,直到找到要找的地址。所以遍历LinkedList使用增强for循环和迭代器的效率高,使用普通for循环会每次都从头开始遍历,效率较差。
AbstractList抽象类
AbstractList 虽然是抽象类,但其内部只有一个抽象方法 get:
1 | |
从字面上看这是获取的方法,子类必须实现它,一般是作为获取元素的用途,除此之外,如果子类要操作元素,还需要重写 add、set、 remove方法,因为 AbstractList 虽然定义了这几个方法,但默认是不支持的。
ArrayList频繁扩容导致添加性能急剧下降,如何处理?
每次扩容都会创建一个数据,将数据复制到新数组,所以在ArrayList中有一个构造方法,参数是自定义长度,指定初始容量。
1 | |
ArrayList插入或删除元素一定比LinkedList慢吗?
不一定,LinkedList 其底层调用了 Node 的方法,该方法循环也是比较复杂的。如果这个 LinkedList 上的数据很多,虽说进行了折半的 但是效率也是比较低的。
而 ArrayList 底层数组需要移动位置,复制数组。
ArrayList是线程安全的吗?
ArrayList不是线程安全的,效率高。
如何解决ArrayList线程安全问题?
可以使用安全集合
Vector。可以使用
Collections工具类中的SyschronizedList方法解决ArrayList的线程安全问题。
定义为全局变量,被多个线程所共享,就要考虑线程问题。
定义为局部变量时,调用方法会在虚拟机栈处创建一个栈帧,虚拟机栈线程私有的,每一次只有一个线程执行,所以局部变量的数据是独立的,不需要考虑安全问题。
如何复制一个ArrayList集合到另一个ArrayList集合中?
可以使用 clone() 方法
使用其中的构造器
使用 addAll() 方法
for 循环遍历复制
已知成员变量集合存储N多用户名称,在多线程的环境下,使用迭代器在读取集合数据的同时如何保证还可以正常的写入数据到集合?
在多线程读写操作,ArrayList 会抛出并发异常,所以在进行读写数据时,使用读写的操作时,使用CopyOnWriteArrayList这个读写分离的集合。
ArrayList和LinkedList区别?
ArrayList
基于动态数组的数据结构
ArrayList支持随机访问查询快,增删慢,但并不一定比
LinkedList慢
LinkedList(双向链表)
基于链表的数据结构
对于顺序操作,
LinkedList不一定比ArrayList慢查询慢,增删快