ArrayList
和LinkedList
的区别是什么?
ArrayList
和LinkedList
都是List
接口的实现类,但它们的底层实现和性能特点有所不同:
-
底层实现:
-
ArrayList
基于动态数组实现,支持快速随机访问。其内部通过Object[]
数组存储元素,当数组容量不足时,会进行扩容。 -
LinkedList
基于双向链表实现,每个节点存储数据和前后节点的引用。它适合频繁的插入和删除操作。
-
-
性能特点:
-
随机访问:
ArrayList
支持快速随机访问,时间复杂度为O(1);而LinkedList
需要从头或尾遍历,时间复杂度为O(n)。 -
插入和删除:
LinkedList
在链表中间插入或删除元素的时间复杂度为O(1),而ArrayList
需要移动元素,时间复杂度为O(n)。
-
-
内存占用:
-
ArrayList
的内存占用相对紧凑,因为它基于数组存储。 -
LinkedList
的内存占用较大,因为每个节点需要额外存储前后节点的引用。
-
-
适用场景:
-
如果需要频繁的随机访问,
ArrayList
是更好的选择。 -
如果需要频繁的插入和删除操作,
LinkedList
更适合。
-
HashMap
的底层实现及扩容机制是什么?
HashMap
是Map
接口的常用实现类,其底层实现和扩容机制如下:
-
底层实现:
-
在JDK 1.8之前,
HashMap
由数组+链表组成,数组是主体,链表用于解决哈希冲突。 -
从JDK 1.8开始,当链表长度超过8时,链表会转换为红黑树,以减少搜索时间。
-
-
扩容机制:
-
当
HashMap
的大小超过阈值(默认为0.75倍的数组长度)时,会触发扩容操作。 -
扩容后的新数组长度是原来的2倍,所有元素会被重新哈希并分配到新的数组中。
-
在扩容过程中,JDK 1.8的哈希算法确保每个元素的新位置要么是原位置,要么是“原位置+原数组长度”,这使得扩容效率较高。
-
-
线程安全性:
-
HashMap
是线程不安全的,多线程环境下可能会出现数据不一致的问题。 -
如果需要线程安全的
Map
,可以使用ConcurrentHashMap
。
-
HashSet
的底层实现是什么?如何判断元素是否重复?
HashSet
是Set
接口的常用实现类,其底层实现和重复判断机制如下:
-
底层实现:
-
HashSet
基于HashMap
实现,底层使用HashMap
来存储元素。 -
每个元素作为
HashMap
的键存储,而值则是一个固定的对象(如PRESENT
)。
-
-
判断元素是否重复:
-
HashSet
通过元素的hashCode()
和equals()
方法来判断是否重复。 -
当尝试添加一个元素时,
HashSet
会先计算该元素的哈希值,然后检查HashMap
中是否已经存在相同的键(即hashCode()
相同且equals()
为true
)。 -
如果存在,则认为该元素重复,不会再次添加;否则,会将该元素作为键存储到
HashMap
中。
-
-
线程安全性:
-
HashSet
是线程不安全的,如果需要线程安全的Set
,可以使用Collections.synchronizedSet
。
-