1. HashMap 的内部实现原理是什么?
HashMap 内部实现原理是数组 + 链表, 通过散列算法将 key 值散列到数组中, 如果到相同的位置, 则通过拉链法解决散列冲突. 在 JDK8 中新增了红黑树结构, 当 HashMap 中的散列冲突链表结构超过 8 个数据时, 会从链表结构转换为红黑树结构.
2. HashMap 的 key 值能否是 null, 如果能, key=null 如何存储以及如何读取的? 如果不能, 为什么?
HashMap 的 key 值可以是 null. 如果 key=null, 则会将它放置在数组下标为 0 的位置.
3. HashMap 如何实现扩容?
HashMap 扩容和初始容器大小与负载因子有关. HashMap 的初始容器大小为 16, 默认的负载因子为 0.75, 当实际容量超过 16*0.75=12 个元素时会进行扩容. 扩容后的容器大小是扩容前的 2 倍, 第一次扩容后的容器大小为 32.
4. 设置 HashMap 的容量有没有注意的地方, 为什么?
指定 HashMap 的容量时, 建议是 2 的幂次方.
HashMap 在寻址是会 key 的 hash 值与容器长度做与运算,(n - 1) & hash. 当 n 的长度为 2 的幂次方时, n-1 的二进制形式就会是 111111, 这样与操作效率会非常的快.
5. HashMap 是否是线程安全的? 如果不是, 多线程下并发操作它可能会带来什么问题? 如果是, 它是怎么实现的?
HashMap 不是线程安全的. 如果在多线程下并发操作不仅会导致脏数据, 甚至可能会造成死循环.(关于死循环产生的原因参考 https://www.cnblogs.com/yulinfeng/p/8558983.html)
6. LinkedHashMap 的内部实现原理是什么? 它是否支持 key=null?
LinkedHashMap 是插入有序的 Map 集合. 它直接继承了 HashMap, 所以很多都直接复用了 HashMap 方法, 所以也支持 key=null. 它在内部除了沿用 HashMap 的底层结构, 还单独维护了一个双向链表, 在对 Map 进行 put 操作时, 同时还会将数据写到了链表的尾部, 保证了插入有序.
7. TreeMap 的内部实现原理是什么? 它是否支持 key=null?
TreeMap 结构也是有序的, 不同的是它是字典有序, 由于它底层是红黑树结构, 插入时会进行比较 key 值的顺序, 所以不允许 key=null 的情况.
8. 介绍下 Hashtable
Hashtable 是线程安全的 Map 类型, 但它的线程安全代价是为整个散列表加锁, 效率很低, 几乎已经废弃. 如果要使用线程安全的 Map, 应该使用 ConcurrentHashMap, 它的实现是分段锁, 能最大的提高效率.
9. 以上三种 Map 类型分别可以应用到哪些场景? 你在哪些场景下使用过?
HashMap 的使用场景很多, 这个使用场景就太多了, 比如用作本地缓存.
LinkedHashMap 因为它的链表结构可以实现 LRU(最近最少使用), 即缓存空间有限, 当元素多余缓存空间, 可淘汰掉最近最少使用的元素. 在 LinkedHashMap 维护了一个 accessOrder 字段, 默认为 false, 当设置为 true 时, 如果访问一个 key 值, 就会将这个元素放置链表头部, 这样在链表尾部的元素就是不常用的元素, 空间不足直接 remove 末尾的元素即可. 所以当要实现 LRU 缓存时, 就可以将 accessOrder 设置为 true 实现.
TreeMap 没有实际应用过, 如果有需要排序的场景则使用 TreeMap
Set
10. HashSet 的内部实现原理是什么, 它有什么特点?
HashSet 集合的特点是不允许有重复的元素, 且无序的, 允许 null 值. 它在内部维护一个 HashMap, 存储在 HashSet 中的元素实际上存储在 HashMap 的 key 中.
11. LinkedHashSet 的内部实现原理是什么, 它有什么特点?
LinkedHashSet 继承自 HashMap, 在内部维护一个双向链表保证插入有序, 允许 null 值.
12. TreeSet 的内部实现原理是什么, 它有什么特点?
TreeSet 是一个有序的集合, 它的作用是提供有序的 Set 集合, TreeSet 是基于 TreeMap 实现的. 不允许有 null 值.
13. 以上三种 Set 类型分别可以应用到哪些场景? 你在哪些场景下使用过?
HashSet 可应用于批量查询时去重.
如果需要返回的数据和入参的数据顺序一致则可以使用 LinkedHashSet.
List
14. ArrayList 的内部实现原理什么?
底层通过数组实现, 创建一个 ArrayList 对象实例时不会初始化数组, 当插入第一条数据时会创建一个大小为 10 的数组.
15. 既然 ArrayList 的底层实现是数组, 那定义 ArrayList 时, 需要定义它的大小吗?
可以不用定义容器的大小, 默认大小为 10, 当容量大小不足时此时将会进行扩容.
16. ArrayList 的扩容机制是什么?
每次新增的容量是旧容量的一半, 扩容后调用 System.arraycopy 方法拷贝到新的数组.
17. 如果初始化 ArrayList 时, 定义一个容量大小为 11, 此时扩容了几次, 容量大小为 16 呢?
不进行扩容.
18. LinkedList 的内部实现原理是什么?
底层通过链表实现, 所以不存在扩容.
19. Vector 和 ArrayList,LinkedList 的区别?
Vector 是线程安全的额, ArrayList,LinkedList 不是线程安全的. Vector 的线程安全是为每个方法加上 synchronized 关键字, 效率不高, 不常用.
20. ArrayList 与 LinkedList 分别可以应用到哪些场景?
大多数情况下使用 ArrayList, 因为 ArrayList 是数组实现, 它随机读取的速度更快, 但插入指定位置慢; LinkedList 由于是链表实现, 所以随机读取的速度慢, 但插入指定位置快.
关注 CoderBuff 公众号, 回复 "面试" 获取更多
来源: https://www.cnblogs.com/yulinfeng/p/11374459.html