1、ArrayList 和 LinkedList 区别
答案
1.底层结构
ArrayList:动态数组;
LinkedList:双向链表。
2.查询
ArrayList:随机访问快,下标 O (1) ;
LinkedList:只能遍历,查询慢 O (n)。
3.增删 ArrayList:中间插入 / 删除要移位,效率低;
LinkedList:只改节点引用,首尾增删快。
4.内存
ArrayList:连续内存,有扩容冗余;
LinkedList:每个节点存前后指针,占用内存更大。
5.使用场景
多查少改用 ArrayList;
频繁首尾增删、少查询用 LinkedList。
2、ArrayList 默认初始容量、扩容机制
答案
默认初始容量 10;
扩容规则:默认扩容为原来的 1.5 倍;
底层 oldCapacity + oldCapacity >> 1。
3、ArrayList 线程安全吗?不安全怎么解决
答案
不安全,多线程并发写会覆盖、数据错乱、报数组越界。解决方式:
Vector(古老、效率低)Collections.synchronizedList包装CopyOnWriteArrayList并发推荐
4、ArrayList 和 Vector 区别
答案
- Vector 所有方法加
synchronized,线程安全但效率低;ArrayList 不安全。 - Vector 默认容量 10,扩容2 倍;ArrayList 扩容 1.5 倍。
- Vector 老旧、性能差,开发基本不用。
5、CopyOnWriteArrayList 原理及适用场景
答案
原理:写时复制。
写操作时先复制一份新数组,在新数组修改,改完把引用指向新数组;
读操作直接读原数组,读写分离,读不加锁。
优点:读极快、并发安全;
缺点:占用双倍内存、实时性弱(读的是旧快照)。
适用:读多写少 并发场景。
6、ArrayList 遍历时能不能删除元素?会报什么错
答案
普通 for 循环随便删;
增强 for / 迭代器遍历中直接 remove ,会触发 ConcurrentModificationException 并发修改异常。
7、为什么会出现并发修改异常?
答案
ArrayList 有 modCount 修改版本号,迭代时会校验;
遍历期间集合被修改,modCount 改变,与预期版本号不一致,直接抛异常。
8、遍历中正确删除元素怎么做
答案
- 用 Iterator 迭代器自带 remove ()
- 倒序 for 循环删除
- Java8
removeIf条件删除
9、Arrays.asList 坑点
答案
- 返回的是 ArrayList 内部私有静态类,不是 java.util.ArrayList;
- 固定大小,不能 add/remove,会抛异常;
- 传入基本类型数组,会被当作单个元素存入。
10、ArrayList 序列化特点
答案
底层数组 transient 修饰,不全部序列化;
只序列化实际有元素的部分,节省序列化开销。
11、LinkedList 能不能当栈、队列用
答案
可以。实现了 Deque 接口,可作为:
- 栈:push/pop
- 队列:offer/poll
- 双端队列:首尾都能进出
面试一句话总结
ArrayList 底层数组、查询快增删慢、默认容量 10、扩容 1.5 倍、线程不安全;LinkedList 双向链表、首尾增删快查询慢;并发读多写少用 CopyOnWriteArrayList;遍历直接删会触发并发修改异常,要用迭代器或倒序循环删除。