一、简要介绍 Java 集合框架的整体架构
1.Java 集合框架主要分为两大接口体系:Collection
和 Map
。
2.Collection
是单列集合的根接口,下面又有三个子接口,分别是 List
(有序、可重复)、Set
(无序、不可重复)和 Queue
(队列)。
3.Map
是双列集合的根接口,用于存储键值对。
4.以下是java集合的基础架构图
5.Java 集合框架的核心继承关系图(文本描述版)
-
├─ Collection 接口(单列集合的根接口)
-
│ ├─ List 接口(有序、可重复)
-
│ │ ├─ ArrayList(动态数组实现,非线程安全)
-
│ │ ├─ LinkedList(双向链表实现,非线程安全)
-
│ │ └─ Vector(动态数组实现,线程安全,已过时)
-
│ │ └─ Stack(继承自 Vector,后进先出栈结构)
-
│ ├─ Set 接口(无序、不可重复)
-
│ │ ├─ HashSet(基于哈希表,非线程安全)
-
│ │ │ └─ LinkedHashSet(HashSet + 链表维护插入顺序)
-
│ │ └─ TreeSet(基于红黑树,支持排序)
-
│ └─ Queue 接口(队列,FIFO 原则)
-
│ ├─ PriorityQueue(优先级队列,基于堆结构)
-
│ └─ Deque 接口(双端队列)
-
│ └─ LinkedList(实现 Deque)
-
└─ Map 接口(双列集合,键值对存储)
-
├─ HashMap(基于哈希表,非线程安全)
-
│ └─ LinkedHashMap(HashMap + 链表维护顺序)
-
├─ TreeMap(基于红黑树,支持键排序)
-
└─ Hashtable(线程安全,已过时,不允许 null 键值)
-
└─ Properties(继承自 Hashtable,用于配置文件)
二、Collection
和 Collections
有什么区别?
Collection是Java集合框架中的(顶层)接口,表示一组对象的集合。它定义了集合的基本行为,如添加、删除、遍历等操作。
Collections是一个工具类,提供了一系列静态方法来操作各种集合对象,包括对集合排序、查找最大最小值、反转、随机化等操作。它并不是一个集合接口或类,而是一组方法的集合,用于方便对集合进行操作。
以下是对Collection和Collections的一些代码使用介绍
import java.util.ArrayList;
import java.util.Collections;
import java.util.Collection;
public class CollectionExample {
public static void main(String[] args) {
// 创建一个ArrayList集合
Collection<String> list = new ArrayList<>();
// 向集合中添加元素
list.add("Apple");
list.add("Banana");
list.add("Orange");
// 使用Collections工具类对集合进行操作
System.out.println("原始集合:" + list);
// 使用Collections工具类进行排序
Collections.sort((List<String>) list);
System.out.println("排序后的集合:" + list);
// 使用Collections工具类进行反转
Collections.reverse((List<String>) list);
System.out.println("反转后的集合:" + list);
}
}
AI写代码java运行
篇幅限制下面就只能给大家展示小册部分内容了。整理了一份核心面试笔记包括了:Java面试、Spring、JVM、MyBatis、Redis、MySQL、并发编程、微服务、Linux、Springboot、SpringCloud、MQ、Kafka 面试专题
需要全套面试笔记【点击此处即可】免费获取
三、List、Set和Map之间的区别是什么?
List是有序集合,可以包含重复元素。
Set是无序集合,不允许重复元素。
Map是键值对的集合,每个键唯一对应一个值,键和值都可以是任意对象。
四、List的子类有哪些?它们之间有什么区别?
- ArrayList、LinkedList、Vector是List的子类。
- ArrayList是基于动态数组实现的,适合随机访问和遍历。
- LinkedList是基于链表实现的,插入和删除操作效率高。
- Vector是线程安全的动态数组,通常不推荐使用,可以使用ArrayList代替。
五、 Set的子类有哪些?它们之间有什么区别?
- HashSet、TreeSet、LinkedHashSet是Set的子类。
- HashSet是基于哈希表实现的,无序且不允许重复元素。
- TreeSet是基于红黑树实现的,元素有序且不允许重复,支持自然排序和定制排序。
- LinkedHashSet是基于哈希表和链表实现的,具有插入顺序。
六、 Queue的常见实现类有哪些?它们之间有什么区别?
- LinkedList、PriorityQueue是Queue的常见实现类。
- LinkedList可以作为队列和栈来使用,实现了Queue和Deque接口。
- PriorityQueue是优先级队列,根据元素的自然顺序或自定义比较器排序。
七、 Map的常见实现类有哪些?它们之间有什么区别?
- HashMap、TreeMap、LinkedHashMap是Map的常见实现类。
- HashMap基于哈希表实现,无序且允许键和值为null。
- TreeMap基于红黑树实现,键有序且不允许键为null,支持自然排序和定制排序。
- LinkedHashMap基于哈希表和双向链表实现,保持插入顺序或访问顺序。
八、常用集合线程安全总结表格
接口 | 实现类 | 线程安全 | 替代方案 | 特点 | 适用场景 |
---|---|---|---|---|---|
List | ArrayList | 非线程安全 | Collections.synchronizedList() CopyOnWriteArrayList | 动态数组,查询快、增删慢,允许null 元素 | 单线程环境下的快速访问 |
LinkedList | 非线程安全 | Collections.synchronizedList() CopyOnWriteArrayList | 双向链表,增删快、查询慢,允许null 元素 | 频繁增删的单线程场景 | |
Vector | 线程安全(过时) | CopyOnWriteArrayList | 动态数组,线程安全,性能低,已被Collections.synchronizedList() 替代 | 遗留代码兼容 | |
Stack | 线程安全(过时) | 使用Deque 实现(如 ArrayDeque) | 继承自 Vector,后进先出(LIFO)结构 | 栈结构需求,但推荐使用Deque | |
Set | HashSet | 非线程安全 | Collections.synchronizedSet() ConcurrentHashSet | 基于哈希表,无序且不可重复,允许null 元素 | 单线程下快速查找和去重 |
TreeSet | 非线程安全 | Collections.synchronizedSortedSet() ConcurrentSkipListSet | 基于红黑树,有序且不可重复,不允许null 元素 | 单线程下需要排序的场景 | |
LinkedHashSet | 非线程安全 | 无直接替代,需手动同步 | 维护插入顺序,基于哈希表 + 链表 | 需要有序且去重的单线程场景 | |
Map | HashMap | 非线程安全 | Collections.synchronizedMap() ConcurrentHashMap | 基于哈希表,键唯一,允许null 键值 | 单线程下快速键值对操作 |
TreeMap | 非线程安全 | Collections.synchronizedSortedMap() ConcurrentSkipListMap | 基于红黑树,键有序,不允许null 键 | 单线程下需要键排序的场景 | |
Hashtable | 线程安全(过时) | ConcurrentHashMap | 线程安全,不允许null 键值,性能低,已被ConcurrentHashMap 替代 | 遗留代码兼容 | |
LinkedHashMap | 非线程安全 | 无直接替代,需手动同步 | 维护插入顺序或访问顺序,适合缓存场景 | 需要有序键值对的单线程场景 |
九、ArrayList 和 LinkedList 的区别?
关键点
- 数据结构:
ArrayList
是动态数组,LinkedList
是双向链表。- 增删性能:
LinkedList
头尾操作快,ArrayList
中间操作慢。- 查询性能:
ArrayList
通过索引直接访问,查询快。
扩展
ArrayList
默认容量为 10,扩容为原容量的 1.5 倍。
十、HashMap 的底层结构?
关键点
- JDK 1.7:数组 + 链表。
- JDK 1.8:数组 + 链表 + 红黑树(链表长度≥8 且容量≥64 时转换)。
扩展
- 扩容触发条件:当前元素数量超过
loadFactor
(默认 0.75)× 容量。- 扩容为 2 倍,需重新计算哈希值并迁移数据。
篇幅限制下面就只能给大家展示小册部分内容了。整理了一份核心面试笔记包括了:Java面试、Spring、JVM、MyBatis、Redis、MySQL、并发编程、微服务、Linux、Springboot、SpringCloud、MQ、Kafka 面试专题
需要全套面试笔记【点击此处即可】免费获取
十一、HashSet 和 TreeSet 的区别?
关键点
HashSet
基于哈希表,无序;TreeSet
基于红黑树,有序。TreeSet
不允许null
元素,HashSet
允许。
十二、HashMap 和 Hashtable 的区别?
关键点
Hashtable
线程安全(synchronized
),不允许null
键值;HashMap
非线程安全,允许null
键值。ConcurrentHashMap
替代Hashtable
,性能更高。
十三、ConcurrentHashMap 的实现原理?
关键点
- JDK 1.7:分段锁(
Segment
),每个Segment
独立加锁。- JDK 1.8:
CAS
+synchronized
,锁粒度细化到节点。
扩展
ConcurrentHashMap
支持高并发,吞吐量远高于Hashtable
。
十四、说说Vector 和 ArrayList 的区别?
Vector
线程安全(synchronized
),扩容为原容量的 2 倍;ArrayList
非线程安全,扩容为 1.5 倍。
十五、说说什么是hash冲突,并说说如何解决hash冲突
(PS:如何回答此面试题,说说鄙人之见)
1.定义hash冲突
哈希冲突是指不同的键经过哈希函数计算后得到相同的哈希值,导致它们被分配到哈希表的同一个桶中。
概念很长,可能不太好理解,可以借着以下的话来辅助理解:
哈希冲突就好比是在分配房间号的时候,不同的人由于名字相同(键经过哈希函数计算后得到相同的哈希值),导致他们被分配到了同一个房间(哈希表的同一个桶)里面。
2. 列举解决hash冲突的方法
常见hash冲突解决方法总结,如下表格
方法 | 原理 | 典型应用场景 | 优缺点 |
---|---|---|---|
链地址法(Separate Chaining) | 将同一桶中的元素用链表或红黑树存储,冲突时追加到链表尾部或树中。 | Java 的HashMap 和HashSet (JDK 1.8 后链表长度超过 8 转为红黑树)。 | 优点:实现简单,冲突处理高效; 缺点:链表过长时查询性能下降。 |
开放定址法(Open Addressing) | 冲突时在哈希表中寻找下一个可用位置(通过探测序列)。 | Java 的ThreadLocal.ThreadLocalMap 。 | 优点:节省空间,无需额外存储结构; 缺点:可能导致 “二次聚集”,影响性能。 |
再哈希法(Rehashing) | 冲突时使用不同的哈希函数重新计算哈希值,直到找到可用桶。 | 部分数据库索引优化场景。 | 优点:减少哈希冲突概率; 缺点:多次计算哈希值增加开销。 |
建立公共溢出区 | 所有冲突元素统一存储在另一个溢出区,主哈希表只存储非冲突元素。 | 早期哈希表实现(如 C 语言中的某些库)。 | 优点:结构简单; 缺点:溢出区访问效率低,维护复杂。 |
扩容(Resizing) | 当哈希表负载因子超过阈值时,扩容哈希表并重新分配元素(即 “再哈希”)。 | Java 的HashMap (默认负载因子 0.75)。 | 优点:降低冲突概率,提升整体性能; 缺点:扩容操作耗时,需权衡扩容阈值。 |
3.结合具体场景举例
HashSet 和 HashMap 使用链地址法存储冲突元素。当链表长度超过 8 时,会转换为红黑树以提升查询效率。此外,HashMap 的扩容机制会动态调整哈希表大小,减少冲突的发生。
4.解决哈希冲突的核心
选择高效的冲突处理策略和合理的哈希表参数(如负载因子),以平衡空间和时间复杂度。