Java集合100道面试真题(背诵完整版)

第一部分 基础概念(1-15 题)

1.Java 集合分为哪两大类

答:Java集合顶层分为Collection单列集合Map双列集合 两大类。

1、Collection(单列集合):存放单个独立元素,继承Iterable接口,支持迭代遍历,包含List、Set、Queue三大子体系,绝大多数实现类非线程安全;

2、Map(双列集合):存放Key-Value键值对映射关系,Key唯一不可重复、Value允许重复,不继承Iterable,不能直接foreach遍历;

补充:Java集合底层全部依托数组、链表、红黑树、跳表、堆五大数据结构实现。

2.Collection 和 Collections 区别

答:二者本质完全不同,一个是顶层接口 ,一个是工具类

1、Collection:是Java集合单列顶层根接口,派生List、Set、Queue,定义集合通用增删查改方法;

2、Collections:是java.util包下的集合工具类,静态方法居多,不可实例化;

补充:常用方法:排序、二分查找、集合反转、批量加锁、生成不可变集合,专门辅助操作集合。

3.List、Set、Queue 三者区别

答:三者都是Collection单列集合,核心特性、使用场景差异极大:

1、List(列表):有序、可重复、带有下标索引 ,支持根据下标精准查询,常用实现类:ArrayList、LinkedList;

2、Set(集合):无序、不可重复、无下标索引 ,主要用于数据去重,常用实现类:HashSet、LinkedHashSet、TreeSet;

3、Queue(队列):遵循先进先出FIFO规则 ,用于缓冲、排队、线程通信,常用实现类:ArrayDeque、阻塞队列;

补充:三者绝大多数实现类非线程安全,并发场景需使用JUC并发集合。

4.迭代器作用

答:统一集合遍历方式,支持遍历中安全删除元素。

5.foreach 循环底层原理

答:foreach 是Java增强for循环,底层完全基于迭代器Iterator实现 ,编译阶段编译器会自动生成迭代器代码。

1、语法简洁,无需手动定义索引、无需获取迭代器;

2、遍历过程中不允许修改集合元素个数 (增/删),否则抛出并发修改异常;

3、适用所有实现Iterable接口的单列集合,不支持普通数组下标操作、无法获取遍历下标。

6.fail-fast 快速失败原理

答:fail-fast 是集合的并发修改保护机制,用于防止集合在遍历过程中被非法修改。

1、集合内部维护一个 modCount 修改计数器 ,每次增删元素都会自增;

2、迭代器初始化时会记录当前修改次数 expectedModCount;

3、遍历期间一旦发现 modCount != expectedModCount,直接抛出 ConcurrentModificationException 并发修改异常;

补充:普通非线程安全集合(ArrayList、HashSet)默认采用该机制,**快速抛出异常、避免脏数据**。

7.fail-safe 安全失败原理

答:fail-safe 是安全失败遍历机制,多用于并发集合,遍历过程**不会抛出并发修改异常**。

1、遍历不直接操作原集合,会**拷贝一份数据快照**进行遍历读取;

2、采用读写分离思想:读操作访问快照,写操作修改原集合;

3、遍历期间无法感知集合最新改动,存在数据弱一致性

补充:典型实现类:CopyOnWriteArrayList、CopyOnWriteArraySet,适合读多写少的并发场景。

8.isEmpty 和 size ()==0 区别

答:功能一致,isEmpty 执行效率更高,优先使用。

9.数组与集合区别

答:数组长度固定,集合长度可动态扩容;数组可存基本类型与对象,集合只存对象。

10.泛型作用

答:泛型是Java编译期类型约束机制,本质用于约束集合存储的数据类型。

1、保障类型安全 :编译阶段校验数据类型,防止存入错误数据;

2、避免强制类型转换 :取出元素无需手动强转,简化代码;

3、消除泛型擦除带来的隐患 :提高代码可读性、复用性;

补充:泛型只在编译期生效,运行期会发生泛型擦除,不存在泛型类型。

11.Iterable 和 Iterator 区别

答:二者为迭代相关核心接口,职责分工明确,缺一不可。

1、Iterable(可迭代接口):位于java.lang包,是集合顶层接口,内部仅有iterator()方法,用于生成迭代器 ;实现该接口代表当前集合允许被遍历;

2、Iterator(迭代器接口):位于java.util包,拥有hasNext()、next()、remove()方法,负责执行具体遍历逻辑 ,完成元素判断、获取、删除;

补充:foreach循环语法要求集合必须实现Iterable接口,这也是Map不能直接foreach遍历的根本原因。

12.ListIterator 特点

答:支持正向、反向双向遍历,遍历中可修改集合元素。

13.同步集合与并发集合区别

答:二者均为线程安全集合,底层加锁机制、并发性能差距极大。

1、同步集合:采用全局独占锁 (synchronized),锁住整个集合,并发时所有线程互斥等待,吞吐量低、性能差;典型实现:Vector、HashTable、synchronizedList;

2、并发集合:采用细粒度锁 + CAS乐观锁 ,仅锁定局部节点,无锁竞争、读写分离,高并发吞吐量高、性能优异;典型实现:ConcurrentHashMap、CopyOnWriteArrayList;

补充:业务开发优先使用并发集合,淘汰老旧同步集合。

14.不可变集合特点

答:创建后无法增删改元素,天然线程安全。

15.Arrays.asList 常见坑

答:返回固定长度集合,不能新增删除;传入基本类型数组会出现解析异常。

第二部分 List 集合(16-35 题)

16. ArrayList 底层实现

答:ArrayList 底层基于可变长度的 Object 动态数组 实现,是 List 接口最常用实现类。

1、JDK1.7 之后采用懒加载机制 ,初始化创建空常量数组,不占用内存;

2、首次添加元素时自动扩容为默认容量10,后续按规则动态扩容;

3、内存地址连续,支持随机快速访问,增删需要后置位移复制;

补充:非线程安全,适合查询多、增删少、单线程业务场景。

17.ArrayList 扩容机制

答:ArrayList 采用1.5倍动态扩容机制 ,底层依靠数组拷贝完成数据迁移。

1、JDK1.7及以上采用懒加载,初始化为空数组,首次add添加元素时,自动初始化容量为10;

2、当集合元素个数size超过数组容量阈值,触发自动扩容,新容量为原容量的1.5倍;

3、底层调用Arrays.copyOf(),开辟新数组、拷贝旧数组全部元素,迁移完成后回收旧数组;

补充:数组扩容无法原地扩容,频繁扩容会产生大量无用数组、耗费性能;大数据量业务建议手动指定初始化容量,规避扩容损耗。

18.ArrayList 默认空数组原因

答:懒加载设计,初始化不占用内存,节省系统资源。

19. ArrayList 最大存储容量

答:Integer.MAX_VALUE - 8。

20. ArrayList 查询快增删慢原因

答:内存连续查询快;中间位置增删需要大量移动元素,效率低。

21.LinkedList 底层结构

答:LinkedList 底层基于双向循环链表 实现,无固定容量、不涉及数组扩容机制。

1、内部定义Node节点类,包含item数据、prev前驱指针、next后继指针,双向指向节点;

2、维护头尾指针,可快速定位首尾元素,物理内存不连续,仅靠指针关联节点;

3、无扩容机制,每新增一个元素单独创建节点,动态占用内存;

补充:不支持随机访问,没有索引快速定位,遍历只能从头尾节点开始查找。

22.LinkedList 优缺点

答:首尾增删效率极高,随机查询遍历效率极低。

23.ArrayList 与 LinkedList 区别

答:二者同为 List 实现类,底层结构不同,性能与使用场景差异明显。

1、底层结构:ArrayList 基于动态数组 ;LinkedList 基于双向链表

2、查询性能:ArrayList 内存连续,支持随机索引访问,查询速度极快;LinkedList 无索引,只能逐一遍历,查询慢;

3、增删性能:ArrayList 中间增删需移动大量元素,效率低;LinkedList 仅修改指针引用,头尾增删速度快;

4、内存占用:ArrayList 数组预留冗余空间,存在内存浪费;LinkedList 节点保存指针,额外内存开销更大;

补充:日常开发查询多、遍历多优先用ArrayList;频繁头尾增删、极少查询选用LinkedList,二者均非线程安全。

24.Vector 集合特点

答:方法加锁线程安全,默认 2 倍扩容,性能低下,现已淘汰。

25.不推荐使用 Stack 原因

答:底层依赖 Vector 性能差,推荐使用 ArrayDeque 替代栈结构。

26.三种线程安全 List 集合

答:Java 提供三种线程安全的 List,分别为 Vector、synchronizedList、CopyOnWriteArrayList,底层加锁机制与性能差异巨大。

1、Vector:古老集合,所有方法加 synchronized 全局锁,锁粒度大、并发性能差,扩容2倍,生产基本淘汰;

2、Collections.synchronizedList:通过装饰器模式包装普通List,同样采用全局锁,迭代遍历依旧需要手动加锁,并发性能一般;

3、CopyOnWriteArrayList:JUC并发集合,采用写时复制+读写分离,无并发修改异常,读多写少场景性能优异;

补充:优先级排序:CopyOnWriteArrayList > synchronizedList > Vector,生产禁止使用Vector。

27.CopyOnWriteArrayList 核心原理

答:底层采用写时复制 + 读写分离 并发思想,是JUC包下线程安全的List集合。

1、读操作:直接读取原数组,无锁、并发读取效率极高;

2、写操作:添加、删除、修改时,先对原数组进行完整拷贝 生成新数组,在新数组中完成修改;

3、修改完成后将原数组引用指向新数组,写操作采用ReentrantLock可重入锁保证线程安全;

补充:遍历读取的是旧数组快照,不会抛出并发修改异常,存在数据弱一致性,只适合读多写少业务场景。

28.CopyOnWriteArrayList 缺点

答:频繁写入内存占用高,遍历数据存在弱一致性。

29.ArrayList 线程不安全表现

答:多线程并发添加出现元素覆盖、数组越界、数据丢失问题。

30.创建 ArrayList 指定初始化容量好处

答:提前分配内存,减少频繁扩容次数,提升运行效率。

31.List 集合遍历删除元素注意事项

答:仅迭代器可删除,禁止普通 for、foreach 循环删除元素。

32.List 集合快速去重方式

答:日常开发常用两种主流去重方式,分别适配有序、无序业务场景,简单高效。

1、LinkedHashSet 去重:利用其元素不可重复、保留插入顺序的特性,有序去重 ,代码简洁、无额外依赖;

2、Java8 Stream 流式去重:使用distinct()方法,底层基于hashCode+equals判断,写法极简,支持链式编程;

补充:HashSet去重会打乱顺序,若需保留原顺序优先使用LinkedHashSet;自定义对象去重必须重写hashCode和equals方法。

33.List 批量添加数据优化方案

答:批量添加数据核心优化思路为减少集合扩容次数、降低数组拷贝开销 ,提升批量写入性能。

1、提前预估批量数据总量,初始化集合时指定合理初始容量,规避多次自动扩容;

2、优先使用addAll()批量添加方法,底层优化拷贝逻辑,比循环add()效率更高;

3、大批量数据可采用分批写入,避免单次加载海量数据造成内存峰值过高;

补充:ArrayList每次扩容都会产生新数组、拷贝元素,大批量数据不指定容量会严重损耗性能,是开发高频优化点。

34.ArrayDeque 优势

答:高性能双端队列,替代栈与普通队列,不允许存储 null 值。

35.List 集合常用排序方式

答:开发中常用两种主流排序方式,适配普通对象、自定义对象,写法简洁、性能优异。

1、Collections工具类排序:使用Collections.sort(),支持自然排序与自定义Comparator比较器,底层为优化后的归并排序,适配JDK低版本;

2、Java8 Stream流式排序:通过sorted()方法排序,代码极简、链式编程,结合Lambda表达式简化自定义比较逻辑,可读性更强;

补充:排序建议优先使用Stream排序;自定义对象排序必须指定比较器,防止类型转换异常,且排序后不改变原集合顺序。

第三部分 Set 集合(36-55 题)

36.Set 集合通用特点

答:无序、元素不可重复、无下标索引。

37.HashSet 底层实现原理

答:HashSet 底层直接封装 HashMap ,本质是简化版的 HashMap,专为单列去重设计。

1、内部维护一个 HashMap 成员变量,存入集合的元素,最终作为 HashMap 的 Key 进行保存;

2、为节约内存,所有元素对应的 Value 统一存入一个静态空 Object 常量占位;

3、继承哈希表特性,无序、不可重复,查询存取速度快,不保证元素存储顺序;

补充:依赖 HashMap 的哈希机制完成去重,去重逻辑完全遵循 hashCode() + equals() 双重校验规则。

38.HashSet 去重核心原理

答:HashSet 依托哈希表机制 实现元素去重,采用 hashCode() + equals() 双重校验判定元素是否重复。

1、首先调用元素hashCode()方法计算哈希值,通过位运算确定数组下标;哈希值不同,直接判定为新元素,完成存入;

2、若哈希值相同,发生哈希冲突,进一步调用equals()方法做内容比对;

3、equals返回true判定元素重复,舍弃当前元素;返回false判定为不同元素,挂在链表尾部;

补充:先比哈希再比内容,**hashCode用于快速筛选、equals用于精准判定**;自定义对象必须手动重写这两个方法,否则去重失效。

39.自定义对象存入 Set 必须重写方法

答:必须重写 hashCode () 与 equals (),否则无法完成正确去重。

40.LinkedHashSet 特点

答:LinkedHashSet 继承自 HashSet,是有序且不可重复 的集合,底层依托 LinkedHashMap 实现。

1、底层结构:哈希表 + 双向链表,额外维护一条双向链表记录元素插入顺序;

2、排序特性:保留元素插入顺序 ,不具备自动排序能力,存取顺序一致;

3、去重规则:完全沿用HashSet的hashCode()+equals()双重校验去重机制;

补充:性能略低于HashSet,链表会产生额外内存开销;适合需要保留插入顺序+数据去重的业务场景。

41.TreeSet 底层原理

答:TreeSet 底层直接封装 TreeMap ,是具备自动排序能力的单列集合,底层依托红黑树数据结构实现。

1、内部维护TreeMap对象,存入元素作为TreeMap的Key,Value统一使用静态空对象占位;

2、底层基于红黑树 ,具备自动平衡、天然排序、查询效率高的特点,时间复杂度O(logN);

3、不保留插入顺序,根据元素规则自动升序排序,依赖比较器判定元素大小与重复;

补充:非线程安全,不允许存入null元素,排序方式分为自然排序、自定义比较器排序,去重依据compareTo()方法返回值。

42.TreeSet 两种排序方式

答:TreeSet 包含自然排序(Comparable)自定义比较器排序(Comparator) 两种排序方式,优先级区分明确。

1、自然排序:元素实体类实现Comparable接口,重写compareTo()方法,定义默认比较规则,适用于固定排序逻辑;若未实现接口直接存入,会抛出类型转换异常;

2、自定义比较器排序:创建TreeSet时传入Comparator外部比较器,重写compare()方法,灵活定制排序规则,无需修改元素实体类源码;

补充:优先级规则:外部比较器Comparator 高于 内部比较器Comparable;TreeSet依靠比较方法返回值判定重复,返回0即判定元素重复。

43.TreeSet 去重判定依据

答:compareTo 方法返回 0 即判定元素重复。

44.Set 集合快速选型

答:开发中根据去重、顺序、排序 三大业务需求快速选型,三款常用Set实现类分工明确、界限清晰。

1、HashSet:底层哈希表,无序无排序,存取速度最快;仅做单纯数据去重、不关心元素顺序时优先使用,日常开发使用率最高;

2、LinkedHashSet:哈希表+双向链表,保留插入顺序;需有序去重 、保证存取顺序一致场景选用,性能略低于HashSet;

3、TreeSet:底层红黑树,自带自然排序;无需手动排序、要求元素按规则升序排列时选用,支持自定义比较器定制排序逻辑;

补充:三者均非线程安全;并发去重场景优先使用CopyOnWriteArraySet,禁止直接使用普通Set。

45.EnumSet 集合特点

答:EnumSet 是专为枚举类量身定制 的专用Set集合,也是所有Set集合中性能最优、内存占用最小的实现类。

1、存储限制:仅允许存放枚举类型元素,不支持存入其他任意类型对象,类型约束极强;

2、底层结构:底层采用位向量实现,占用极小内存,运算效率极高,远超HashSet、TreeSet;

3、排序特性:天然按照枚举定义顺序排序,有序且不可重复,无哈希冲突、无树结构开销;

补充:非线程安全,并发场景需手动同步;日常开发枚举批量筛选、遍历场景优先选用,不存放枚举绝不使用。

46.Set 集合允许存储 null 值吗

答:HashSet 允许存储一个 null,TreeSet 不允许存储 null。

47.TreeSet 禁止 null 原因

答:存入 null 调用比较方法直接触发空指针异常。

48.HashSet 是否线程安全

答:非线程安全,高并发环境禁止直接使用。

49.Java 中有序 Set 实现类

答:LinkedHashSet。

50.HashSet 默认初始化参数

答:HashSet 底层封装 HashMap,初始化参数完全沿用 HashMap 底层常量。

1、默认初始容量:16,集合底层哈希表默认数组长度,且容量必须保持2的幂次方;

2、默认负载因子:0.75,作为扩容阈值,当元素占用量达到容量75%触发扩容;

3、默认扩容倍数:2倍,每次扩容容量翻倍,重新哈希散列迁移元素;

补充:负载因子0.75是时间与空间平衡值,兼顾哈希冲突概率与内存利用率,生产不建议随意修改。

51.负载因子 0.75 设计优势

答:负载因子0.75是开发者权衡哈希冲突概率、内存空间利用率 得出的最优临界值。

1、若负载因子过大(如1.0):数组填满才扩容,极度节省内存,但哈希冲突概率剧增,链表长度变长,查询效率大幅下降;

2、若负载因子过小(如0.5):提前频繁扩容,减少哈希冲突,但大量数组空间闲置,造成严重内存浪费;

3、0.75平衡临界点:兼顾内存利用率与哈希冲突概率,散列分布均匀,时间复杂度与空间复杂度达到最优平衡;

补充:该数值为源码固定常量,生产环境不建议手动修改,改动会破坏哈希表最优性能结构。

52.负载因子大小带来的影响

答:数值越大冲突越多,数值越小内存浪费越多。

53.Set 集合主流遍历方式

答:日常开发中Set集合共有三种主流遍历方式,适配不同业务场景,无下标不可通过普通for循环遍历。

1、迭代器遍历:通过iterator()获取迭代器,手动判断遍历、获取元素,遍历过程中可安全删除元素,兼容性最强;

2、foreach遍历:语法简洁、代码极简,底层基于迭代器实现,适合简单遍历;禁止遍历中增删元素,防止触发并发修改异常;

3、Stream流式遍历:JDK8新增特性,结合Lambda表达式,支持过滤、排序、统计等链式操作,代码优雅可读性高;

补充:优先推荐foreach与Stream遍历;需要遍历删除元素强制使用迭代器,杜绝非法修改引发异常。

54.Set 集合转 List 集合写法

答:开发中常用三种Set转List方式,适配不同JDK版本,语法简洁、应用场景区分明确。

1、构造方法转换:new ArrayList<>(set),最简单通用,底层批量拷贝元素,适合绝大多数普通转换场景;

2、Java8 Stream流转:set.stream().collect(Collectors.toList()),支持转换中途过滤、排序、去重等链式操作,灵活性最高;

3、Collections工具类:Collections.addAll(),适合手动指定固定容量集合,减少扩容开销;

补充:转换后无序Set变为有序List,顺序取决于原Set底层结构;生产优先使用构造方法,需要加工数据优先使用Stream流转。

55.集合排序优先级

答:Java集合中存在两种比较排序接口,明确外部比较器Comparator 优先级高于 内部比较器Comparable

1、Comparable:内部比较器,绑定实体类内部,重写compareTo()方法,定义固定默认排序规则,侵入实体类源码;

2、Comparator:外部比较器,独立于实体类之外,重写compare()方法,灵活临时修改排序规则,无需改动源码;

3、优先级执行逻辑:若同时存在两种比较规则,优先采用外部Comparator比较逻辑,内部Comparable规则会被覆盖;

补充:开发固定排序用Comparable,临时多变排序优先用Comparator,TreeSet、TreeMap均遵循该优先级规则。

第四部分 Queue 队列集合(56-70 题)

56.队列核心特点

答:遵循先进先出 FIFO 原则。

57.ArrayDeque 核心特点

答:数组实现双端队列,性能强悍,禁止存储 null 元素。

58.PriorityQueue 优先级队列原理

答:PriorityQueue 是无界优先级队列 ,底层基于可变长度数组实现的最小堆 ,可自动按照优先级排序元素。

1、底层结构:采用动态数组存储堆节点,物理结构为数组,逻辑结构为完全二叉树,默认构建最小堆;

2、排序规则:默认自然升序排序,堆顶为最小值;支持自定义Comparator比较器,灵活实现最大堆;

3、存取逻辑:入队自动上浮调整堆结构,出队移除堆顶元素后自动下沉重构堆,保证堆顶永远为极值;

补充:非线程安全、不允许存入null元素,不保证遍历顺序;适合任务权重排序、定时优先级处理场景,并发优先级推荐使用PriorityBlockingQueue。

59.阻塞队列核心作用

答:实现生产者消费者模型,队列满自动阻塞、队列空自动阻塞唤醒。

60.Java 七大阻塞队列

答:JUC包下提供七大阻塞队列,全部实现BlockingQueue接口,天然支持生产者消费者阻塞模型,常用于线程池、消息缓冲、流量削峰。

1、ArrayBlockingQueue:有界数组阻塞队列 ,固定容量、单锁机制,读写共用一把锁,并发性能一般,公平锁可选;

2、LinkedBlockingQueue:无界链表阻塞队列 ,默认无上限、双锁机制,读写分离锁,并发吞吐量高,线程池默认常用队列;

3、SynchronousQueue:无容量同步队列 ,不存储元素,一对一线程传递,无缓冲,适合瞬时高并发任务;

4、DelayQueue:延时无界阻塞队列 ,基于优先级堆,元素到期才能取出,用于订单超时、定时任务;

5、PriorityBlockingQueue:优先级无界阻塞队列 ,底层最小堆,自动排序,适合带权重优先级任务处理;

6、TransferQueue:精准传输阻塞队列 ,生产者等待消费者接收完毕才返回,保障数据百分百被消费;

7、LinkedBlockingDeque:双向链表阻塞双端队列 ,头尾均可存取,适合工作窃取、双向任务调度;

补充:有界队列防止无限扩容内存溢出,无界队列无需手动设置容量;线程池高频使用前三种阻塞队列。

61.数组阻塞队列与链表阻塞队列区别

答:数组有界单锁,链表无界双锁。

62.SynchronousQueue 队列特点

答:SynchronousQueue 是无容量、无缓冲阻塞队列 ,也是七大阻塞队列中最特殊的同步传输队列。

1、存储特性:内部不维护任何存储容器,容量永久为0,无法缓存数据,生产者存入元素后必须等待消费者消费;

2、传输模式:严格一对一线程匹配,做到生产与消费实时握手,无多余积压任务;

3、锁机制:采用CAS非阻塞算法,吞吐量极高,支持公平与非公平两种传输模式;

补充:适合瞬时高并发、任务无积压、快速转发场景;Executors.newCachedThreadPool()线程池底层默认使用该队列,不适合大批量缓冲任务。

63.DelayQueue 延时队列常用场景

答:订单超时取消、定时延时任务、会员到期处理。

64.队列四类存取 API

答:抛出异常、返回布尔值、永久阻塞、超时阻塞。

65.双向队列适用场景

答:双向队列(Deque)具备头尾双向存取、既可队列又可栈 的特性,两端均可执行增删操作,灵活性远高于普通单向队列。

1、栈结构模拟:利用队首入队、队首出队,实现后进先出逻辑,替代老旧Stack类,执行效率更高;

2、普通队列业务:遵循先进先出,做常规数据缓冲、排队处理,适配简单流量削峰;

3、首尾高频操作:频繁在集合头部、尾部新增或删除元素,如消息头尾插入、历史记录追加;

4、工作窃取场景:多线程任务调度,线程可从队列两端获取任务,均衡线程负载;

补充:常用实现类为ArrayDeque、LinkedBlockingDeque,日常优先使用ArrayDeque,性能优于链表实现。

66.阻塞队列与非阻塞队列区别

答:队列满空时处理策略不同,阻塞队列会阻塞线程。

67.线程池常用工作队列

答:线程池内置三款核心工作队列,适配不同并发流量、任务积压场景,均为阻塞队列,配合线程池实现任务缓冲与复用。

1、ArrayBlockingQueue:有界数组队列,容量固定、单锁机制;适合流量可控、防止无限堆积 场景,手动设置队列长度,达到容量上限触发拒绝策略,避免内存溢出;

2、LinkedBlockingQueue:无界链表队列,双锁并发高、吞吐量强;默认无上限,适合持续平稳、无突峰 业务,CachedThreadPool、FixedThreadPool底层默认使用;

3、SynchronousQueue:无容量同步队列,不缓存任务;适合瞬时高并发、无任务积压 场景,每来一个任务必须有线程处理,newCachedThreadPool专属队列;

补充:日常业务优先有界队列防止OOM;流量平稳用链表队列;瞬时爆并发采用同步队列,严格区分业务选型。

68.PriorityBlockingQueue 介绍

答:PriorityBlockingQueue 是无界优先级阻塞队列 ,结合了优先级排序与阻塞队列两大特性,是线程安全的优先级队列。

1、底层结构:底层基于可变长度数组实现的最小堆,逻辑为完全二叉树,自动对元素进行优先级排序,无固定容量限制;

2、排序规则:默认自然升序、堆顶为最小值,支持自定义Comparator比较器灵活修改排序权重;

3、阻塞特性:队列空时出队线程阻塞,队列满时不会阻塞,因无界可无限扩容,不会触发拒绝策略;

补充:内部采用ReentrantLock保证线程安全,不允许存入null元素;适合多线程下权重任务排序、定时优先级调度场景,缺点是海量数据扩容会消耗内存。

69.使用队列实现栈思路

答:借助两个队列来回转移元素完成后进先出。

70.使用栈实现队列思路

答:拆分入栈与出栈两个栈,实现先进先出。

第五部分 Map 集合(71-90 题)

71.Map 集合整体结构

答:Map是双列键值对集合 ,顶层为Map根接口,不继承Iterable接口,无法直接迭代遍历,整体分为三大分支实现类,存储K-V键值映射关系。

1、底层层级:顶层Map接口,派生抽象父类AbstractMap,再衍生三大主流实现分支;

2、无序哈希分支:HashMap、LinkedHashMap、WeakHashMap,底层依托哈希表,查询速度快,无序或保留插入顺序;

3、有序树形分支:TreeMap,底层依托红黑树,可根据Key自动排序,存取时间复杂度O(logN);

4、古老线程分支:HashTable、Properties,古老同步集合,采用全局锁,性能差基本淘汰;

补充:通用特性:Key唯一不可重复、Value可重复,允许单个null键、多个null值(除并发Map外);高并发专用ConcurrentHashMap,单独属于JUC并发包,采用分段锁优化并发读写。

72.HashMap JDK1.7 与 JDK1.8 核心区别

答:JDK1.8 针对1.7底层结构、插入逻辑、哈希算法、并发缺陷做全方位优化,是HashMap历史性升级,核心区别分为五点。

1、底层结构:1.7采用数组+单向链表 ;1.8采用数组+单向链表+红黑树 ,链表过长自动树化,优化高哈希冲突查询效率;

2、元素插入:1.7采用头插法 ,新节点插入链表头部;1.8改为尾插法 ,新节点追加链表尾部,避免并发循环链表死循环;

3、哈希算法:1.7扰动次数多、逻辑繁琐;1.8简化哈希扰动,一次高位异或运算,兼顾散列效果与运算效率;

4、扩容迁移:1.7扩容后全部元素重新哈希散列;1.8优化迁移逻辑,元素仅拆分至原位置、原位置+旧容量两处,无需重复计算哈希;

5、树化机制:1.7无树化逻辑,链表无限变长查询退化;1.8满足阈值自动树化、扩容触发降级,平衡查询性能;

补充:二者均非线程安全;1.7最大痛点为并发下头插法导致循环链表、CPU占用飙升,1.8彻底修复该致命缺陷。

73.HashMap 核心常量参数

答:默认容量 16、负载因子 0.75、树化阈值 8、降级阈值 6、最小树化容量 64。

74.HashMap 完整 put 存入流程

答:以JDK1.8源码逻辑为准,HashMap存入元素历经哈希计算、下标定位、冲突处理、树化判断、扩容校验 五大核心流程,步骤清晰、面试高频必考。

1、哈希计算:对Key做哈希扰动运算,高位异或低位,减少哈希冲突;若Key为null,哈希值固定为0;

2、下标定位:通过位运算 (容量-1) & 哈希值,快速计算元素在数组中的存储下标;

3、判断数组节点:若该下标位置为空,直接新建普通节点存入元素,流程结束;

4、处理哈希冲突:若下标位置已有元素,判断节点类型;若是红黑树节点,直接插入红黑树并平衡;若是链表节点,循环遍历链表,逐一比对key;

5、元素覆盖/新增:遍历链表过程中,key相同则直接覆盖旧value;遍历至链表末尾无重复key,追加新节点;

6、树化判定:新增节点后判断链表长度,链表≥8且数组容量≥64,自动树化为红黑树;

7、扩容校验:全部元素插入完成后,集合size自增,判断是否超过扩容阈值,超出则触发2倍扩容,重新迁移元素。

补充:全程采用尾插法,无链表反转死循环风险;插入优先覆盖重复key,保证Key唯一性,是HashMap核心存储逻辑。

75.哈希扰动函数作用

答:让哈希值高位参与运算,大幅降低哈希冲突概率。

76.采用 (n-1)&hash 计算下标原因

答:HashMap 采用 (容量-1) & 哈希值 位运算计算数组下标,替代传统取模运算,核心目的为提升运算效率、保证散列均匀

1、运算效率极高:位运算直接操作二进制底层,无需除法、取余运算,CPU执行速度远快于 % 取模运算,极致优化存取性能;

2、散列分布均匀:因容量固定为2的幂次方,(n-1)二进制全部为1,与hash做与运算可完整保留哈希值低位,使下标分布更均匀,减少哈希冲突;

3、简化扩容迁移:扩容后元素仅需判断高位哈希位,自动拆分至原下标或原下标+旧容量处,无需重新复杂计算下标;

补充:该公式硬性依赖容量为2的幂次方,若容量非2的幂,(n-1)二进制存在0空位,会导致部分下标永远无法存入,造成空间浪费、冲突暴涨。

77.HashMap 容量必须为 2 的幂次方

答:HashMap 硬性规定底层数组容量必须为2的幂次方 ,是适配位运算、优化哈希散列、简化扩容逻辑的底层设计,缺一不可。

1、适配位运算下标:依靠公式 (n-1)&hash 计算下标,容量为2的幂时,n-1二进制全部补1,哈希值低位完整保留,散列均匀;若非2的幂,二进制存在空位0,导致部分下标永久无法存储,造成空间浪费、冲突暴涨;

2、简化扩容迁移:扩容永远翻倍,扩容后元素仅通过哈希高位判断分流,自动拆分至原下标、原下标+旧容量两处,无需重复计算哈希,迁移效率极高;

3、优化哈希散列:2的幂容量搭配高位扰动算法,让元素分散更均匀,有效降低链表过长概率,优化查询性能;

补充:若手动传入非2的幂初始化容量,底层会自动向上取最近的2的幂;该底层设计牺牲少量存储空间,换取极致运算性能,是时间与空间的经典取舍。

78.HashMap 扩容执行流程

答:以JDK1.8源码为准,HashMap采用2倍扩容、高低位拆分迁移 机制,无需重新计算哈希,大幅优化扩容性能,流程规范且面试高频。

1、扩容触发时机:集合元素个数size超过扩容阈值(容量 * 负载因子0.75),触发自动扩容;若链表树化、数组容量不足64,也会优先扩容;

2、创建新数组:原容量向左位移一位,容量翻倍,同时更新扩容阈值;

3、元素拆分迁移:遍历原数组所有节点,根据哈希值高位特征拆分元素;借助二进制高位判断,将元素分为低位组、高位组;

4、高低位分流:低位哈希位为0,保留在原下标 ;高位哈希位为1,迁移至原下标+旧容量 位置;

5、节点迁移规则:链表节点采用尾插法顺序迁移,避免链表反转;红黑树节点拆分后,节点数量≤6自动降级为链表;

6、引用替换:全部元素迁移完毕,将原数组引用指向新数组,等待GC回收旧数组内存;

补充:JDK1.8扩容无需重新hash,仅判断高位;彻底规避1.7头插法导致的并发循环链表问题,扩容安全性、迁移效率大幅提升。

79.JDK1.7 HashMap 并发死循环原因

答:多线程环境下头插法,链表反转形成循环链表。

80.HashMap 支持 null 键 null 值

答:仅允许一个 null 作为 key,value 可无限存入 null。

81.LinkedHashMap 核心特点

答:LinkedHashMap 是有序可重复双列集合 ,直接继承HashMap,在哈希表基础上额外维护双向链表,兼顾查询性能与顺序特性,原生支持LRU缓存淘汰逻辑。

1、底层结构:哈希表 + 双向链表 ,复用HashMap数组、链表、红黑树结构,额外新增头尾指针串联所有节点;

2、排序模式:包含两种排序规则,默认插入顺序 、访问顺序可手动开启;访问顺序模式下,被访问节点自动移至链表尾部;

3、去重规则:沿用HashMap机制,Key唯一不可重复,允许存储null键、null值;

4、LRU实现:重写removeEldestEntry方法可自定义缓存容量,自动淘汰最久未使用节点,轻松实现简易缓存;

补充:非线程安全,性能略低于HashMap,链表产生少量额外内存开销;业务常用于有序键值存储、本地简易缓存、数据追溯场景。

82.TreeMap 底层原理

答:TreeMap 是可排序双列集合 ,底层基于红黑树 数据结构实现,天然根据Key进行有序排序,适合需要键值自动排序的业务场景。

1、底层结构:采用自平衡二叉查找树(红黑树),每个节点包含Key、Value、左子节点、右子节点、父节点、颜色标记,时间复杂度稳定O(logN);

2、排序规则:默认按照Key自然升序排序,支持自定义外部比较器;排序逻辑、去重规则完全复用TreeSet比较机制;

3、去重逻辑:依靠比较器返回值判定重复,返回0判定Key重复,直接覆盖旧Value,不依赖hashCode与equals方法;

4、节点维护:插入、删除元素后自动左旋、右旋、变色,完成红黑树自平衡,防止树结构退化成链表;

补充:非线程安全,不允许存入null类型Key,允许存储null值;对比HashMap无序特性,TreeMap牺牲存取速度换取有序性,适合需要持续排序、范围查找的业务。

83.HashTable 被淘汰核心原因

答:全表全局锁并发性能极差,不支持 null 键值。

84.Properties 集合作用

答:专门用于读取项目 properties 配置文件。

85.WeakHashMap 实现原理

答:WeakHashMap 是弱引用键的哈希映射集合 ,底层基于哈希表实现,核心特性为Key采用弱引用机制,自动回收无效缓存数据,专为内存敏感缓存场景设计。

1、引用机制:Key统一包装为弱引用对象,在JVM垃圾回收时,若Key无外部强引用,无论内存是否充足,都会被GC强制回收;

2、底层结构:采用数组+单向链表结构,无红黑树、无树化逻辑,算法保留JDK1.7简单哈希架构,结构轻量化;

3、自动清理:内部绑定引用队列,被GC回收的Key会进入队列,每次增删改查时主动清除失效Entry,自动清理空洞、释放内存;

4、存储限制:Value为强引用,必须依赖Key存活;若Key被回收,强引用Value会随节点一并清除,避免内存泄漏;

补充:非线程安全、允许null键null值;典型用途:临时缓存、本地脱敏缓存、资源解绑容器,适合短期临时数据存储,无需手动删除数据。

86.JDK1.7 ConcurrentHashMap 底层

答:JDK1.7版本采用分段锁Segment机制 ,核心思想为拆分容器、分散锁竞争,在HashTable全局锁基础上大幅优化并发性能。

1、底层结构:采用Segment数组 + 哈希表 双层结构,外层为分段数组Segment,内层每一段独立维护哈希表(数组+单向链表);

2、分段锁原理:默认分割16个Segment分段,每一段独立持有一把ReentrantLock可重入锁,不同分段线程互不抢占锁,并发度最高支持16个线程同时写入;

3、存取逻辑:写入数据先通过哈希定位至指定Segment分段,加锁后再对内层哈希表完成存取,仅锁定当前分段,不影响其他分段;

4、初始化参数:默认分段数量16、不可扩容,单个分段默认容量16、负载因子0.75,扩容仅针对单个分段独立扩容;

补充:劣势明显,分段数量固定无法动态扩容、内存占用偏高、哈希冲突严重链表过长;JDK1.8彻底舍弃分段锁,改用细粒度节点锁优化并发能力。

87.JDK1.8 ConcurrentHashMap 底层

答:JDK1.8彻底舍弃1.7分段锁,采用数组+单向链表+红黑树 结构,结合CAS乐观锁 + synchronized悲观锁 实现细粒度加锁,大幅提升并发吞吐量。

1、底层结构:和HashMap一致,采用数组、链表、红黑树三层结构,保留树化、降级机制,哈希算法、高位拆分扩容逻辑完全复用;

2、加锁机制:摒弃重量级Segment分段锁,只锁定数组当前下标首节点 ,锁粒度极度细化,锁竞争大幅降低;无冲突采用CAS自旋写入,冲突严重启用synchronized加锁;

3、节点优化:新增TreeBin、ForwardingNode特殊节点,分别标记红黑树根节点、扩容迁移节点,辅助并发扩容;

4、扩容机制:支持多线程协助扩容,检测到扩容状态的线程会主动帮忙迁移数据,加快扩容进度,避免单线程迁移瓶颈;

5、辅助类设计:内部保留CounterCell计数单元格,分散统计元素个数,解决高并发下size统计卡顿问题;

补充:不允许存储null键null值,默认初始容量16、负载因子0.75;相较于1.7,内存占用更低、并发度更高、冲突处理更优秀,是生产高并发键值存储首选集合。

88.ConcurrentHashMap 不允许 null 键值原因

答:高并发下无法区分数据为空还是数据不存在。

89.Map 集合最高效遍历方式

答:Map主流存在四种遍历方式,entrySet遍历方式综合效率最高 ,尤其适合大批量键值对遍历,生产环境优先推荐。

1、entrySet遍历(最优):一次性获取键值对实体Entry,只遍历一次集合,同时拿到Key与Value,无二次查询开销,大数据量性能碾压其他方式;底层直接复用Map内部Entry节点,减少IO查询次数;

2、keySet遍历:先遍历全部Key,再通过get()方法二次查询Value,需两次遍历;哈希表还要重复计算哈希下标,海量数据下性能损耗严重;

3、values遍历:仅单纯遍历全部Value,无法获取对应Key,适用只取值、不要键的极简业务;

4、迭代器/ForEach遍历:语法简洁,底层依旧依托entrySet实现,小数据量可读性高,大数据量无性能优势;

补充:开发规范:大数据量优先entrySet、只取value用values、禁止keySet大批量遍历;JDK8推荐forEach链式遍历,代码简洁且底层优化完善。

90.HashMap 线程安全替代方案

答:优先 ConcurrentHashMap,其次使用同步包装 Map。

第六部分 集合高阶面试题(91-100 题)

91.大数据量 HashMap 初始化优化

答:大数据量场景下HashMap核心优化手段为手动指定初始化容量 ,精准规避多次自动扩容,减少数组拷贝、哈希重计算带来的性能损耗。

1、计算公式:初始化容量 = 预估数据量 / 0.75 + 1;加一是为了预留冗余空间,防止临界值触发多余扩容;

2、底层原理:HashMap默认负载因子0.75,元素达到阈值就会二倍扩容;大数据量不指定容量,会频繁扩容、产生大量废弃数组、加重GC压力;

3、容量规整:若计算结果非2的幂次方,底层自动向上取最近2的幂,保证符合底层位运算存储逻辑;

4、开发规范:预估数据量大小时,强制手动初始化容量;杜绝默认空参构造,海量数据可适当微调负载因子降低哈希冲突;

补充:该优化是生产高频硬性规范,能大幅减少内存碎片、提升大数据量存取吞吐量,是最简单高效的HashMap调优手段。

92.HashMap 链表转红黑树条件

答:JDK1.8 HashMap 链表转红黑树必须同时满足两个硬性条件 ,缺一不可,目的是平衡查询性能与内存开销。

1、链表长度条件:当前链表节点数量 ≥ 8 ;链表过长、哈希冲突严重,线性查询效率退化,需要转为红黑树优化查询速度;

2、数组容量条件:底层哈希数组容量 ≥ 64 ;若数组容量过小,优先进行2倍扩容,不触发树化,避免小树结构频繁转换浪费性能;

3、底层设计原因:阈值8源自泊松分布统计,哈希冲突链表长度达到8概率极低,判定为恶意哈希碰撞;红黑树节点占用内存远大于普通链表节点,低容量树化性价比极低;

补充:仅满足链表长度≥8、数组容量不足64时,只会扩容不会树化;树化后满足降级条件(节点≤6)自动退回链表,中间7作为缓冲临界值,防止频繁树化抖动。

93.红黑树退化为链表条件

答:JDK1.8 HashMap 在扩容迁移 时触发红黑树降级,仅判断节点数量,阈值明确、机制严谨,与树化条件形成双向制衡。

1、降级触发条件:红黑树节点数量 ≤ 6 ,自动退化为普通单向链表;

2、触发时机:不会在普通删除时降级,仅在数组二倍扩容、拆分迁移树节点过程中校验并降级;

3、缓冲阈值设计:节点数量7作为中间缓冲区间,既不树化也不降级;防止节点数量在6~8之间频繁波动,造成反复树化、降级抖动,浪费CPU性能;

4、底层设计原因:红黑树节点结构复杂、占用内存更高,节点数量少时,链表遍历速度更快、维护成本更低,无需保留树结构;

补充:树化双条件、降级单条件,树化严苛、降级宽松;取舍目的为尽量少树化、尽量晚降级,减少结构转换开销,优化综合存取性能。

94.Java 实现 LRU 缓存淘汰算法

答:LRU(最近最少使用)是缓存淘汰策略 ,核心思想:优先淘汰**最久未被访问**的数据,日常最简实现方式为继承LinkedHashMap,重写判定移除方法,无需手动维护链表逻辑。

1、底层实现原理:依托LinkedHashMap哈希表+双向链表 结构,开启访问排序模式;每访问一个元素,自动将该节点移动至链表尾部,链表头部永远为最久未使用数据;

2、核心重写方法:重写removeEldestEntry()方法,自定义缓存最大容量;当集合元素超过设定容量,自动移除链表头部最老旧未访问节点;

3、手写实现步骤:①继承LinkedHashMap;②构造方法开启accessOrder访问排序;③定义最大缓存容量;④重写移除规则判定方法;

4、优缺点:实现极简、代码量少、底层由JDK优化;不适合海量高并发缓存,线程不安全,海量数据建议使用Redis的LRU淘汰机制;

补充:除原生实现外,可手动通过HashMap+双向链表手写LRU,自主维护插入、删除、移动节点逻辑,常用于面试手写算法考题。

95.集合引发内存泄漏常见原因

答:Java集合本身不会自动释放引用,若使用不当会造成对象常驻内存、GC无法回收 ,引发内存泄漏,生产环境高频出现四大核心原因。

1、静态集合长期持有引用:static修饰的List、Map等集合生命周期跟随虚拟机,全局常驻内存;存入对象后永不主动清空,对象一直被强引用指向,GC无法回收,不断堆积内存;

2、ThreadLocal集合未手动移除:ThreadLocal绑定线程,线程池线程长期存活;存放集合数据后不调用remove(),弱引用key被回收、value强引用滞留,造成内存泄漏;

3、集合扩容产生冗余废弃数组:ArrayList、HashMap频繁扩容,不断产生旧数组垃圾对象;若业务持续高频写入且不做优化,大量废弃数组堆积加重GC压力,形成隐性内存泄漏;

4、自定义对象未断引用:集合存入实体对象,对象关联外部资源(流、连接、监听器);仅清空集合不手动置空引用,资源对象无法回收,常驻堆内存;

补充:规避方案:非必要不使用静态集合、用完集合手动clear清空、ThreadLocal使用完毕强制remove、大批量数据提前指定初始化容量、及时断开无用对象引用。

96.多线程集合使用规范

答:多线程环境下禁止使用普通非线程安全集合,需根据读写比例、并发量级、业务场景 规范选用并发集合,规避并发异常、数据覆盖、内存溢出问题,生产通用规范如下。

1、List集合规范:读多写少场景优先CopyOnWriteArrayList ,写时复制保证线程安全,遍历无并发修改异常;高频频繁增删、读写均衡场景,手动加锁普通ArrayList,减少拷贝开销;坚决淘汰Vector、synchronizedList,全局锁并发性能极差。

2、Map集合规范:高并发键值存储强制使用ConcurrentHashMap ,细粒度锁+CAS适配高并发读写;少量低并发简单场景可使用Collections.synchronizedMap;彻底弃用HashTable,全局锁效率低下且不允许空键值。

3、Queue队列规范:线程池、生产者消费者模型使用阻塞队列 ;流量管控、防止OOM选用有界ArrayBlockingQueue;平稳持续任务选用LinkedBlockingQueue;瞬时无积压高并发选用SynchronousQueue;延时定时任务采用DelayQueue。

4、通用开发禁忌:多线程禁止并发修改普通集合,避免触发ConcurrentModificationException;并发集合迭代遍历无需手动加锁,普通同步集合遍历必须手动加锁;大批量并发写入优先设置有界容量,防止无限扩容内存溢出。

补充:核心选型口诀:读多写少用拷贝、高并发映射用Concurrent、线程通信用阻塞、老旧同步集合全淘汰

97.Java8 Stream 集合常用操作

答:Stream是JDK8推出的流式处理机制 ,依托Lambda表达式简化集合代码,采用流水线思想处理数据,分为中间操作、终端操作,代码极简、链式编程、延迟执行,生产开发高频使用。

1、常用七大核心操作:

①过滤(filter):条件筛选元素,剔除不符合规则的数据,常用于数据清洗;

②映射(map):类型转换、字段提取,将A对象转为B对象,抽取实体指定字段;

③排序(sorted):支持自然排序、自定义比较器排序,灵活调整元素顺序;

④去重(distinct):底层重写hashCode+equals,实现集合快速去重;

⑤分组(groupingBy):按照指定字段分组,返回Map结构,适合分类统计;

⑥统计(collect/count):归集转换、计数求和、最值查找,实现数据汇总;

⑦遍历(forEach):简洁遍历集合,替代传统循环,代码可读性高。

2、操作分类特性:

①中间操作:filter、map、sorted、distinct,延迟执行、可链式拼接,不会立即触发执行;

②终端操作:forEach、collect、count、max/min,触发流水线执行,关闭流、不可重复使用。

3、开发规范:

Stream流只能一次性使用 ,终端执行后流自动关闭;并行流(parallelStream)适合无锁纯计算、无线程安全依赖场景;禁止在流中修改原集合数据。

补充:Stream彻底简化集合加工代码,替代冗余循环判断,是Java8最核心优化特性;日常开发优先使用Stream处理集合筛选、转换、排序、分组业务。

98.大数据量集合处理优化

答:大数据量集合处理核心优化思想:避免全量加载、减少内存占用、降低拷贝开销、控制GC压力 ,防止一次性加载海量数据引发OOM内存溢出、卡顿、频繁GC,生产通用优化方案如下。

1、数据加载优化:禁止一次性加载全量数据,采用分批读取、分页查询、流式读取 ;数据库分页、文件逐行读取,截断数据源流量,控制单次内存负载;

2、集合初始化优化:创建集合手动指定初始化容量 ,套用公式:初始容量=预估量/0.75+1;规避频繁扩容产生数组拷贝、内存碎片,减少GC次数;

3、遍历逻辑优化:海量数据优先迭代器、Stream流式遍历 ,不使用普通for、foreach一次性加载;遍历中及时释放无效引用,禁止遍历中扩容、新增元素;

4、数据去重与裁剪:提前过滤无效空数据、冗余重复数据;使用HashSet、Stream去重,减少集合存储压力;非必要字段不存入集合,精简对象内存;

5、内存回收优化:数据处理完毕手动调用clear()清空集合、置空引用;大集合及时断开对象引用,方便GC快速回收,杜绝内存累积泄漏;

6、并发场景优化:海量并发读写选用ConcurrentHashMap、CopyOnWriteArrayList ;禁止普通非线程安全集合;大批量写入采用有界队列,限制最大承载量防止无限膨胀;

补充:硬性开发规范:大数据量杜绝ArrayList无参构造、禁止内存一次性承载全量数据源、杜绝循环频繁创建集合对象;必要时使用第三方流式框架分片处理超大集合。

99.JDK8 集合新增核心特性

答:JDK8针对单列、双列集合进行大规模功能性升级,新增流式操作、便捷删除、键值合并、默认方法 等特性,简化代码、减少冗余循环、提升集合处理效率,五大高频核心特性如下。

1、Stream流式操作:新增java.util.stream流式包,依托Lambda实现筛选、映射、排序、分组、去重、归集链式操作,延迟执行、代码极简,替代传统for循环处理集合;

2、removeIf批量删除:Collection通用新增方法,根据自定义条件批量删除集合元素,底层迭代器安全删除,规避普通遍历删除并发异常,替代手写迭代器删除逻辑;

3、merge键值合并(Map专属):解决重复key覆盖问题,若key不存在直接存入;key存在自定义新旧value合并规则,适合统计累加、数据聚合场景;

4、forEach遍历方法:所有集合重写forEach方法,结合Lambda极简遍历,底层封装迭代器,代码简洁无冗余,禁止遍历中修改集合;

5、compute/putIfAbsent方法(Map专属):putIfAbsent不存在才存入,compute动态计算覆盖value,精准控制键值对修改逻辑,简化空值判断代码;

补充:底层优化:集合接口新增大量默认方法,不破坏原有实现类;Stream并行流可简易实现多线程批量处理,大数据量集合处理效率大幅提升,是开发日常高频使用特性。

100.Java 集合终极选型总结

答:日常开发集合选型遵循以性能优先、贴合业务、规避老旧集合、严控并发安全 四大原则,根据有序、去重、排序、并发、队列五大业务场景快速选型,通用完整选型规则如下。

1、普通单列List选型:查询遍历优先ArrayList ,底层数组随机访问极快;频繁头尾增删、极少查询选用LinkedList;坚决淘汰Vector、Stack老旧集合;

2、去重Set集合选型:单纯无顺序去重使用HashSet;保留插入顺序去重选用LinkedHashSet ;需要自动升降序排序强制使用TreeSet;枚举专用EnumSet性能最优;

3、双列Map集合选型:普通非并发存取优先HashMap;需要保留访问顺序、简易缓存选用LinkedHashMap;按键自动排序选用TreeMap;高并发生产环境强制使用ConcurrentHashMap;彻底弃用HashTable;

4、队列Queue集合选型:普通双端存取、模拟栈结构选用ArrayDeque;权重排序任务使用PriorityQueue;线程池、生产者消费者选用阻塞队列;延时任务采用DelayQueue;瞬时高并发无积压使用SynchronousQueue;

5、并发安全选型:读多写少采用CopyOnWriteArrayList;高并发键值映射采用ConcurrentHashMap;所有并发业务优先JUC并发集合,杜绝同步老旧集合;

6、特殊场景选型:临时内存缓存、自动回收无用对象使用WeakHashMap;固定不可修改数据使用不可变集合;大批量数据必须手动指定初始化容量;

补充:最简背诵选型口诀:查询用数组、增删用链表、去重用哈希、有序用链表映射、排序用红黑、并发用JUC、队列看阻塞、过期用弱引用

附录:集合高频背诵总结(必背)

一、集合体系总览

Java 集合分为两大类:Collection 单列集合Map 双列集合

Collection 三大分支:List(有序可重复)、Set(无序不可重复)、Queue(队列);

Map 存储键值对:key 唯一,value 可重复。

二、五大底层数据结构(必考背诵)

Java集合全部依托数组、链表、红黑树、跳表、堆 五种基础数据结构实现,分工明确、各司其职,是集合底层核心骨架:

1、数组:内存连续、下标寻址 ,查询极快、中间增删慢;代表集合:ArrayList、ArrayDeque、普通哈希表;

2、链表:内存不连续、指针关联 ,增删快、查询慢;代表集合:LinkedList、LinkedHashMap、阻塞链表队列;

3、红黑树:自平衡二叉查找树 ,查询、增删时间复杂度稳定O(logN);代表集合:TreeMap、TreeSet、JDK1.8HashMap;

4、跳表:多层有序链表,空间换时间,查询效率媲美红黑树、实现简单;代表集合:ConcurrentSkipListMap;

5、堆:逻辑完全二叉树、物理数组存储,自动维护极值;代表集合:PriorityQueue、PriorityBlockingQueue。

补充:集合优化本质就是优化这五种结构,组合使用、取长补短;哈希表=数组+链表/红黑树,是最经典结构组合。

三、线程安全集合汇总

1、List 线程安全集合:

① Vector:古老同步集合,全部方法加synchronized全局锁,扩容2倍,并发性能极差,存在大量冗余方法,生产彻底淘汰;

② Collections.synchronizedList:装饰器模式包装普通List,加全局同步锁,遍历需手动加锁,读写性能一般,适合极低并发简单场景;

③ CopyOnWriteArrayList:JUC并发集合,写时复制+读写分离,无锁读取、加锁写入,遍历无并发修改异常,适合读多写少 业务。

2、Map 线程安全集合:

① HashTable:古老集合,全局锁锁住整张哈希表,并发吞吐量极低,不允许null键null值,现已淘汰;

② ConcurrentHashMap:生产高并发首选,JDK1.7分段锁、JDK1.8节点锁+CAS,锁粒度极小、并发性能强悍,不允许null键值。

3、Queue 线程安全集合:

七大阻塞队列(BlockingQueue),全部位于JUC包,内置锁机制,天然支持生产者消费者模型;包含ArrayBlockingQueue、LinkedBlockingQueue、SynchronousQueue、DelayQueue、PriorityBlockingQueue等,线程池专用队列,自带阻塞、唤醒机制。

补充:线程安全集合选型优先级:JUC并发集合 > 同步装饰集合 > 老旧同步集合;生产禁止使用Vector、HashTable。

四、HashMap 必考参数(面试死记)

HashMap 全部固定源码常量,不可修改、面试高频默写,所有底层逻辑围绕这五大常量展开:

1、默认初始化容量:16 ,底层数组默认长度,强制为2的幂次方;

2、默认负载因子:0.75 ,扩容临界阈值,元素占用达到容量75%触发二倍扩容,时间空间最优平衡点;

3、链表树化阈值:8 ,单个链表节点大于等于8,且满足数组容量条件,触发红黑树转换;

4、红黑树降级阈值:6 ,扩容迁移时树节点小于等于6,自动退化为单向链表;

5、最小树化容量:64 ,链表长度达标但数组容量不足64,优先扩容、不树化;

补充扩展参数:

① 扩容倍数:2倍扩容 ,永远保持2的幂次方;

② 插入方式:JDK1.7头插法,JDK1.8改为尾插法

③ 哈希扰动:一次高位异或运算,减少哈希冲突;

④ 最大容量:Integer.MAX_VALUE - 8,防止数组内存溢出;

背诵口诀:16容量、0.75因子、8树6降、64最低树化容量、永远二倍扩容

五、面试高频坑点(必考避坑、最容易丢分)

本板块汇总Java集合所有高频易错、面试官高频挖坑、生产常出错知识点,全部硬性背诵,避开开发与面试陷阱:

1、遍历删除坑:foreach、普通for循环禁止增删元素 ,直接抛出ConcurrentModificationException;唯一安全删除方式:迭代器remove()、List.removeIf();

2、去重判断坑:自定义对象存入HashSet、HashMap必须重写hashCode()和equals() ,只重写一个会导致去重失效、内存重复堆积;

3、空值存储坑:HashMap允许一个null键,ConcurrentHashMap完全禁止null键、null值 ;TreeSet/TreeMap不允许null键,直接空指针;

4、数组转集合坑:Arrays.asList()返回原数组内部静态集合,长度固定无法增删 ;传入基本类型数组会被识别为单个对象,导致集合长度为1;

5、集合初始化坑:大数据量禁止空参构造HashMap、ArrayList;频繁扩容产生大量垃圾数组、加重GC,必须手动指定初始化容量;

6、并发使用坑:ArrayList、HashMap、HashSet全部非线程安全;多线程千万别直接使用,会出现数据覆盖、丢失、死循环、CPU飙升;

7、比较器坑:TreeSet、TreeMap排序优先级:外部比较器Comparator > 内部比较器Comparable ;只实现Comparable不写比较规则直接存对象抛类型转换异常;

8、LRU实现坑:LinkedHashMap默认插入顺序,必须开启accessOrder=true 才是访问顺序,才能实现缓存淘汰;不重写移除方法不会自动淘汰数据;

9、扩容坑:HashMap永远2倍扩容、ArrayList1.5倍扩容;HashMap容量强制2的幂,手动传入非2的幂底层自动向上规整;

10、链表树化坑:链表≥8且数组≥64 才树化;只满足链表长度只会扩容,不会转为红黑树,大量面试混淆该条件;

11、集合清空坑:list=null 无法释放内存,只是断开引用;大集合必须使用clear()清空+手动置空,方便GC回收防止内存泄漏;

12、equals坑:集合判断非空必须先判断集合引用,再判断size;isEmpty()效率高于size()==0,生产禁止手写size判断;

13、并行流坑:parallelStream并行流不保证顺序,多线程无锁操作会引发数据错乱,禁止业务随意滥用并行流;

14、阻塞队列坑:无界阻塞队列无限扩容,高并发会造成OOM;生产队列必须使用有界队列 ,设置最大容量保护内存;

补充:集合所有坑点核心总结:遍历不乱删、对象必重写、并发不用普通集合、空值分清规则、大数据必设容量

六、集合使用选型口诀

查多改少 ArrayList;头尾增删 LinkedList;

普通去重 HashSet;有序去重 LinkedHashSet;

排序使用 Tree 系列;枚举专用 EnumSet;

映射取值用 HashMap;有序缓存 LinkedHashMap;

排序映射 TreeMap;弱存缓存 WeakHashMap;

并发映射 ConcurrentHashMap;淘汰哈希 HashTable;

栈结构用 ArrayDeque;阻塞队列做通信; 有界队列防OOM;无界队列存平稳;

延时任务 DelayQueue;权重优先堆队列;

读多写少拷贝集合;高并发下JUC优先;

静态集合谨慎使用;大数据量必设容量;

遍历删除慎用循环;自定义对象必重写;

空值存储分清规则;生产拒绝老旧集合。

相关推荐
快递鸟社区2 小时前
快递鸟海运查询接口全面解析:从入门到精通,助力跨境物流可视化
java·前端·人工智能
Cloud_Shy6182 小时前
Python 数据分析基础入门:《Excel Python:飞速搞定数据分析与处理》学习笔记系列(第十一章 Python 包跟踪器 上篇)
python·数据分析·excel·pandas·matplotlib
星越华夏2 小时前
msvcrt库在pycharm中运行监控键盘操作无效解决办法
ide·python·pycharm
XGeFei2 小时前
python解释器/多线程程序
开发语言·python
青云计划2 小时前
JVM从入门到精通
java·jvm
甲方大人请饶命2 小时前
Java-面向对象进阶之接口与内部类
java·开发语言·servlet
Dicky-_-zhang2 小时前
分布式缓存实战:Redis与多级缓存架构的完整指南
java·jvm
阿正的梦工坊2 小时前
Kotlin 中的 ?. 和 . 语法详解
开发语言·python·kotlin
墨着染霜华2 小时前
MySQL字符串数字筛选与转换 + Java Integer/Long数值长度避坑指南
java·数据库·mysql