概述
本篇文章主要汇总了继承Collection相关的面试题,这部分面试题也是我面试别人经常会问的一些题目。
面试题目
ArrayList、LinkedList、Vector的区别是什么?
考察点分析:
ArrayList、LinkedList、Vector 3个类都继承了List接口,主要考察对基础知识的了解程度,方便在合适的场景使用不同的容器。
答案:
这个类都继承了List接口,是个有序的集合容器。
ArrayList | LinkedList | Vector | |
底层数据结构 | 数组 | 双向链表 | 数组 |
是否可以存null | 是 | 是 | 是 |
是否线程安全 | 否 | 否 | 是 |
性能 | 随机访问、查询快;在尾部添加效率高,其他地方插入慢 | 插入、删除快;随机访问查询慢; | 性能差,因为用synchronized关键字 |
ArrayList为什么要进行扩容?它的扩容机制是什么样的?
考察点分析:
主要考察你对ArrayList的源码的了解程度。
答案:
ArrayList的底层数据结构就是一个数组,但是java中的是固定长度的,随着ArrayList不断添加元素,这时候就需要对底层的数组进行扩容。
ArrayList扩容的本质就是计算出新的扩容数组的size后实例化,并将原有数组内容复制到新数组中去。默认情况下,新的容量会是原容量的1.5倍。
ArrayList存储元素的数组elementData是transient的,那么它是如何进行序列化?
考察点分析:
主要考察你对集合中的一些细节的了解程度。
答案:
ArrayList实现了Serializable接口,说明可以支持序列化。但是ArrayList底层存放数据的数组对象elementData加了关键字transient,加了这个关键字后,会对这个字段不进行序列化,那ArrayList没有序列化数组对象吗?其实不是的。
实际上ArrayList在序列化的时候会调用writeObject()方法,将size和element写入ObjectOutputStream;反序列化时调用readObject(),从ObjectInputStream获取size和element,再恢复到elementData。
而不是通过elementData来序列化,主要原因在于elementData是一个缓存数组,它通常会预留一些容量,等容量不足时再扩充容量,那么有些空间可能就没有实际存储元素,采用上诉的方式来实现序列化时,就可以保证只序列化实际存储的那些元素,而不是整个数组,从而节省空间和时间。
ArrayList在删除元素的时候,需要注意什么?
考察点分析:
考察你的实战经验。
答案:
在删除元素的时候,不要再for循环中或者foreach中删除元素,可能会出现异常。正确的做法,可以使用迭代器的方式删除或者使用jdk8新提供的removeIf这杨的api。
ArrayList中有个subList方法,这个方法是干嘛的,使用要注意什么?
考察点分析:
考察你的实战经验。
答案:
subList() 方法用于截取并返回动态数组中的一部分发,它是ArrayList的一个子视图,你可以对这个子list添加、删除元素,最终也会影响到原来的list。但是如果你对修改了原来的list,在调用子list的任何操作,都会报异常ConcurrentModificationException。
你知道集合中的Fail-fast机制吗?为什么要有这样的机制?
考察点分析:
考察你对集合中的一些核心设计理念的了解。
答案:
FailFast机制也叫做快速失败机制,它只能被用来检测错误,因为JDK并不保证fail-fast机制一定会发生。当多个线程对同一个集合的内容进行操作时,就可能会产生fail-fast事件。例如:当某一个线程A通过iterator去遍历某集合的过程中,若该集合的内容被其他线程所改变了;那么线程A访问集合时,就会抛出ConcurrentModificationException异常,产生fail-fast事件。
通过记录ArrayList容器的modCount参数来实现。在面对并发的修改时,迭代器很快就会完全失败,而不是冒着在将来某个不确定时间发生任意不确定行为的风险。
总结
以上我根据自己的学习经验以及网上的资料整理汇总出关于List相关的面试题,希望对大家有帮助,后续会持续维护。