JAVA数据结构 DAY1-集合和时空复杂度

本系列可作为JAVA学习系列的笔记,文中提到的一些练习的代码,小编会将代码复制下来,大家复制下来就可以练习了,方便大家学习。

点赞关注不迷路!您的点赞、关注和收藏是对小编最大的支持和鼓励!

系列文章目录

JAVA初阶----------------------------------已更完

JAVA数据结构 DAY1-集合和时空复杂度


目录

目录

系列文章目录

目录

前言

[一、Java 集合框架:数据存储与操作的基石](#一、Java 集合框架:数据存储与操作的基石)

[1.1 什么是集合框架](#1.1 什么是集合框架)

[1.1.1 集合框架的核心结构](#1.1.1 集合框架的核心结构)

[1.1.2 核心组件解析](#1.1.2 核心组件解析)

[1.2 集合框架的重要性:开发与面试双视角](#1.2 集合框架的重要性:开发与面试双视角)

[1.2.1 开发中的核心价值](#1.2.1 开发中的核心价值)

[1.2.2 面试中的高频考点](#1.2.2 面试中的高频考点)

[1.3 集合框架背后的数据结构](#1.3 集合框架背后的数据结构)

[1.3.1 数据结构基础概念](#1.3.1 数据结构基础概念)

[1.3.2 核心集合与数据结构的对应关系](#1.3.2 核心集合与数据结构的对应关系)

[1.3.3 集合框架依赖的 Java 基础知识](#1.3.3 集合框架依赖的 Java 基础知识)

[二、Collection 体系:单列集合的实现与实战](#二、Collection 体系:单列集合的实现与实战)

[2.1 Collection 接口:单列集合的根接口](#2.1 Collection 接口:单列集合的根接口)

[2.2 List 接口:有序可重复的集合](#2.2 List 接口:有序可重复的集合)

[2.2.1 ArrayList:动态数组的实现](#2.2.1 ArrayList:动态数组的实现)

(1)底层原理

(2)核心方法与使用示例

(3)性能特点与适用场景

(4)注意事项

[2.2.2 LinkedList:双向链表的实现](#2.2.2 LinkedList:双向链表的实现)

(1)底层原理

(2)核心方法与使用示例

(3)性能特点与适用场景

[2.2.3 List 实现类对比](#2.2.3 List 实现类对比)

[2.3 Set 接口:无序不可重复的集合](#2.3 Set 接口:无序不可重复的集合)

[2.3.1 HashSet:哈希表的实现](#2.3.1 HashSet:哈希表的实现)

(1)底层原理

(2)核心方法与使用示例

(3)元素唯一性的保证

(4)性能特点与适用场景

[2.3.2 TreeSet:红黑树的实现](#2.3.2 TreeSet:红黑树的实现)

(1)底层原理

(2)核心方法与使用示例

(3)排序规则的实现

(4)性能特点与适用场景

[2.3.3 Set 实现类对比](#2.3.3 Set 实现类对比)

[2.4 Queue 接口:队列结构的实现](#2.4 Queue 接口:队列结构的实现)

[2.4.1 PriorityQueue:优先级队列](#2.4.1 PriorityQueue:优先级队列)

(1)底层原理

(2)核心方法与使用示例

(3)性能特点与适用场景

[2.4.2 ArrayDeque:双端队列](#2.4.2 ArrayDeque:双端队列)

(1)底层原理

(2)核心方法与使用示例

(3)性能特点与适用场景

[三、Map 体系:键值对映射的实现与实战](#三、Map 体系:键值对映射的实现与实战)

[3.1 Map 接口:双列集合的根接口](#3.1 Map 接口:双列集合的根接口)

[3.2 HashMap:哈希表的实现](#3.2 HashMap:哈希表的实现)

[3.2.1 底层原理(JDK1.8+)](#3.2.1 底层原理(JDK1.8+))

[3.2.2 核心参数与扩容机制](#3.2.2 核心参数与扩容机制)

[3.2.3 核心方法与使用示例](#3.2.3 核心方法与使用示例)

[3.2.4 Key 的注意事项](#3.2.4 Key 的注意事项)

[3.2.5 性能特点与适用场景](#3.2.5 性能特点与适用场景)

[3.3 TreeMap:红黑树的实现](#3.3 TreeMap:红黑树的实现)

[3.3.1 底层原理](#3.3.1 底层原理)

[3.3.2 核心方法与使用示例](#3.3.2 核心方法与使用示例)

[3.3.3 性能特点与适用场景](#3.3.3 性能特点与适用场景)

[3.4 Map 实现类对比](#3.4 Map 实现类对比)

[3.5 线程安全的集合方案](#3.5 线程安全的集合方案)

[3.5.1 Collections 工具类包装](#3.5.1 Collections 工具类包装)

[3.5.2 JUC 包中的线程安全集合](#3.5.2 JUC 包中的线程安全集合)

四、算法复杂度:衡量算法效率的标尺

[4.1 算法效率的衡量标准](#4.1 算法效率的衡量标准)

[4.2 时间复杂度](#4.2 时间复杂度)

[4.2.1 时间复杂度的定义](#4.2.1 时间复杂度的定义)

[4.2.2 大 O 渐进表示法](#4.2.2 大 O 渐进表示法)

[(1)推导大 O 阶的三步法](#(1)推导大 O 阶的三步法)

(2)示例解析

[4.2.3 常见时间复杂度及示例](#4.2.3 常见时间复杂度及示例)

[(1)O (1):常数阶](#(1)O (1):常数阶)

[(2)O (logN):对数阶](#(2)O (logN):对数阶)

[(3)O (N):线性阶](#(3)O (N):线性阶)

[(4)O (NlogN):线性对数阶](#(4)O (NlogN):线性对数阶)

[(5)O (N²):平方阶](#(5)O (N²):平方阶)

[(6)O (2ⁿ):指数阶](#(6)O (2ⁿ):指数阶)

[4.2.4 最好、平均、最坏情况](#4.2.4 最好、平均、最坏情况)

[4.3 空间复杂度](#4.3 空间复杂度)

[4.3.1 空间复杂度的定义](#4.3.1 空间复杂度的定义)

[4.3.2 常见空间复杂度及示例](#4.3.2 常见空间复杂度及示例)

[(1)O (1):常数阶](#(1)O (1):常数阶)

[(2)O (N):线性阶](#(2)O (N):线性阶)

[(3)O (logN):对数阶](#(3)O (logN):对数阶)

[(4)O (N²):平方阶](#(4)O (N²):平方阶)

[4.3.3 空间复杂度与时间复杂度的区别](#4.3.3 空间复杂度与时间复杂度的区别)

[4.4 复杂度分析实战示例](#4.4 复杂度分析实战示例)

[4.4.1 示例 1:冒泡排序的复杂度分析](#4.4.1 示例 1:冒泡排序的复杂度分析)

[4.4.2 示例 2:递归阶乘的复杂度分析](#4.4.2 示例 2:递归阶乘的复杂度分析)

[4.4.3 示例 3:斐波那契数列(递归)的复杂度分析](#4.4.3 示例 3:斐波那契数列(递归)的复杂度分析)

五、总结与实战建议

[5.1 集合框架选择指南](#5.1 集合框架选择指南)

[5.2 算法复杂度优化建议](#5.2 算法复杂度优化建议)

[5.3 学习路径建议](#5.3 学习路径建议)

总结


前言

小编作为新晋码农一枚,会定期整理一些写的比较好的代码,作为自己的学习笔记,会试着做一下批注和补充,如转载或者参考他人文献会标明出处,非商用,如有侵权会删改!欢迎大家斧正和讨论!

一、Java 集合框架:数据存储与操作的基石

1.1 什么是集合框架

Java 集合框架(Java Collections Framework,简称 JCF)是定义在java.util包下的一组接口(Interfaces)与实现类(Classes)的总称,也被称为 "容器"(Container)。它的核心作用是将多个元素(Element)封装在一个单元中,提供高效的存储(Store)、检索(Retrieve)、管理(Manipulate)能力,也就是我们常说的增删查改(CRUD)操作。

从现实场景来看,集合框架就像生活中的 "容器":一副扑克牌是牌的集合,一个邮箱是邮件的集合,一个通讯录是姓名与电话的映射集合。在程序开发中,我们需要处理大量类似的 "批量数据",集合框架正是为解决这类问题而生。

1.1.1 集合框架的核心结构

集合框架的整体架构围绕两大核心体系展开,通过接口与实现类的分离设计,实现了 "面向接口编程" 的思想,具体结构如下:

java 复制代码
java.util包核心结构
├─ Iterable(迭代器接口,所有集合的顶层父接口)
│  └─ Collection(单列集合根接口,存储单个元素)
│     ├─ List(有序、可重复、支持索引)
│     │  ├─ ArrayList(动态数组实现)
│     │  ├─ LinkedList(双向链表实现)
│     │  ├─ Vector(线程安全的动态数组,已过时)
│     │  └─ Stack(栈结构,继承Vector,已过时)
│     ├─ Set(无序、不可重复、无索引)
│     │  ├─ HashSet(哈希表实现,基于HashMap)
│     │  ├─ LinkedHashSet(哈希表+双向链表,保证插入顺序)
│     │  └─ TreeSet(红黑树实现,保证元素有序)
│     └─ Queue(队列,遵循FIFO原则)
│        ├─ LinkedList(实现Queue接口,双端队列)
│        ├─ PriorityQueue(优先级队列,基于堆实现)
│        └─ Deque(双端队列接口)
│           └─ ArrayDeque(动态数组实现的双端队列)
└─ Map(双列集合根接口,存储键值对K-V)
   ├─ HashMap(哈希表实现,线程不安全)
   ├─ LinkedHashMap(哈希表+双向链表,保证顺序)
   ├─ TreeMap(红黑树实现,Key有序)
   ├─ Hashtable(线程安全的哈希表,已过时)
   └─ ConcurrentHashMap(线程安全的HashMap,支持高并发)
1.1.2 核心组件解析

集合框架包含三大核心组件,这三大组件共同构成了完整的 "数据存储 - 操作 - 算法" 体系:

  1. 接口(Interfaces) :定义集合的抽象行为,规定 "能做什么",不关心 "怎么做"。例如List接口规定了有序、可重复的特性,Set接口规定了无序、不可重复的特性。
  2. 实现类(Classes) :接口的具体实现,对应底层的数据结构,解决 "怎么做" 的问题。例如ArrayList用动态数组实现List接口,HashSet用哈希表实现Set接口。
  3. 算法(Algorithms) :定义在CollectionsArrays工具类中的静态方法,提供排序、查找、填充等通用操作。例如Collections.sort()用于集合排序,Arrays.binarySearch()用于数组二分查找。

1.2 集合框架的重要性:开发与面试双视角

1.2.1 开发中的核心价值

在实际开发中,集合框架是不可或缺的工具,其价值主要体现在三个方面:

  1. 提升开发效率 :无需手动实现复杂的数据结构(如链表、哈希表),框架已提供经过严格测试、高度优化的实现。例如需要存储动态列表时,直接使用ArrayList即可,无需自己处理数组扩容。
  2. 保证代码质量 :框架实现类由 Java 官方团队开发,性能与稳定性远超大多数开发者临时编写的代码。例如HashMap的哈希冲突解决、扩容机制等,经过了多版本迭代优化。
  3. 增强代码灵活性 :基于接口的设计让代码具备良好的扩展性。例如方法参数定义为List接口,调用者可传入ArrayListLinkedList,无需修改方法内部逻辑。
1.2.2 面试中的高频考点

集合框架是 Java 面试的 "必考题",几乎所有 Java 后台开发岗位都会涉及。从各大厂的面经来看,核心考点集中在以下方向:

  • 腾讯 Java 后台开发面经 :HashMap 实现原理、对象作为 Key 时hashCode()equals()的注意事项、HashSet 与 HashMap 的区别、线程安全的集合方案。
  • 阿里巴巴 Java 后台开发面经:ArrayList 与 LinkedList 的区别、HashMap 的具体实现、HashMap 与 ConcurrentHashMap 的效率对比。
  • 今日头条 Java 后台开发面经:Redis 的 zset 对应 Java 中的集合类型、hashCode () 的作用、集合相关的编程题(如回文链表判断)。

这些问题本质上都是考察对 "集合底层数据结构" 与 "实现原理" 的理解,而非简单的 API 调用。

1.3 集合框架背后的数据结构

集合框架的每个实现类,本质上都是对某种特定数据结构的封装。理解底层数据结构,是掌握集合框架的关键。

1.3.1 数据结构基础概念

数据结构(Data Structure)是计算机存储、组织数据的方式,指相互之间存在一种或多种特定关系的数据元素的集合。常见的数据结构包括数组、链表、栈、队列、哈希表、树等,它们各自有不同的特性与适用场景。

1.3.2 核心集合与数据结构的对应关系
集合类 底层数据结构 核心特性 时间复杂度(增删查)
ArrayList 动态数组(Object []) 有序、可重复、支持索引 查 O (1),增删 O (n)
LinkedList 双向链表 有序、可重复、支持首尾快速操作 查 O (n),增删 O (1)(定位后)
HashSet 哈希表(基于 HashMap) 无序、不可重复、无索引 增删查 O (1)(平均)
TreeSet 红黑树 有序(自然排序)、不可重复 增删查 O (log₂N)
HashMap 数组 + 链表 + 红黑树(JDK1.8+) 无序、Key 唯一、Value 可重复 增删查 O (1)(平均)
TreeMap 红黑树 Key 有序(自然排序)、Key 唯一 增删查 O (log₂N)
PriorityQueue 堆(完全二叉树) 优先级排序、队列结构 入队 O (log₂N),出队 O (log₂N)
ArrayDeque 动态数组(循环数组) 双端队列、支持首尾操作 增删查 O (1)
1.3.3 集合框架依赖的 Java 基础知识

要熟练使用集合框架,需要掌握以下 Java 核心知识点:

  1. 泛型(Generic) :解决集合的类型安全问题,避免强制类型转换。例如List<String>明确集合存储 String 类型,编译期即可检查类型错误。
  2. 自动装箱(Autobox)与拆箱(Autounbox) :基本类型与包装类的自动转换。例如list.add(1)会自动将 int 转为 Integer,int num = list.get(0)会自动将 Integer 转为 int。
  3. Object 的 equals () 方法 :判断对象是否相等,是 Set 去重、Map 键唯一的核心依据。自定义对象存入集合时,必须重写equals()保证逻辑正确性。
  4. Comparable 与 Comparator 接口 :实现对象的排序。Comparable是 "自然排序"(如 String 的字典序),Comparator是 "自定义排序"(如按字符串长度排序)。

二、Collection 体系:单列集合的实现与实战

2.1 Collection 接口:单列集合的根接口

Collection是所有单列集合的顶层接口,定义了操作元素的通用方法,这些方法被所有子接口(List、Set、Queue)继承,具体包括:

  • boolean add(E e):添加元素,成功返回 true
  • boolean remove(Object o):删除指定元素,成功返回 true
  • int size():返回集合元素个数
  • boolean isEmpty():判断集合是否为空
  • void clear():清空集合
  • Iterator<E> iterator():获取迭代器,用于遍历集合

需要注意的是,Collection本身是抽象接口,无法直接实例化,必须通过其子接口的实现类使用。

2.2 List 接口:有序可重复的集合

List接口是Collection的子接口,核心特性是有序 (元素存取顺序一致)、可重复 (允许存储相同元素)、支持索引(通过下标访问元素),类似数组的 "增强版"。

2.2.1 ArrayList:动态数组的实现
(1)底层原理

ArrayList基于动态数组Object[])实现,JDK1.8 及以上版本中,默认初始容量为 10。当元素数量超过数组容量时,会触发扩容机制:

  • 扩容公式:新容量 = 旧容量 + 旧容量右移 1 位(即旧容量的 1.5 倍)
  • 扩容过程:创建新数组 → 将原数组元素拷贝到新数组 → 指向新数组
  • 例如:初始容量 10 → 扩容后 15 → 再次扩容后 22(15+7=22)
(2)核心方法与使用示例
java 复制代码
// 创建ArrayList集合(指定泛型为String)
List<String> list = new ArrayList<>();

// 1. 添加元素
list.add("Java"); // 末尾添加,返回true
list.add(1, "Python"); // 索引1插入,后续元素后移
System.out.println(list); // 输出:[Java, Python]

// 2. 获取元素
String first = list.get(0); // 获取索引0元素,返回"Java"

// 3. 修改元素
list.set(0, "Go"); // 将索引0元素改为"Go"
System.out.println(list); // 输出:[Go, Python]

// 4. 删除元素
list.remove(1); // 删除索引1元素,返回"Python"
System.out.println(list); // 输出:[Go]

// 5. 遍历集合(三种方式)
// 方式1:for循环(利用索引)
for (int i = 0; i < list.size(); i++) {
    System.out.println(list.get(i));
}

// 方式2:增强for循环
for (String elem : list) {
    System.out.println(elem);
}

// 方式3:迭代器
Iterator<String> iterator = list.iterator();
while (iterator.hasNext()) {
    String elem = iterator.next();
    System.out.println(elem);
}
(3)性能特点与适用场景
  • 优点:随机访问效率极高(时间复杂度 O (1)),通过数组下标直接定位元素;遍历速度快,适合批量读取。
  • 缺点:插入 / 删除元素效率低(时间复杂度 O (n)),尤其是中间位置的操作,需要移动后续所有元素;扩容时会产生内存开销,可能浪费空间。
  • 适用场景读多写少的场景,例如数据展示列表、高频读取的配置缓存、数据库查询结果存储等。
(4)注意事项
  1. 初始容量优化 :如果预知元素数量,创建时指定容量可减少扩容次数。例如new ArrayList<>(100),避免多次数组拷贝。
  2. 线程安全问题ArrayList是非线程安全的,多线程环境下并发修改会抛出ConcurrentModificationException。解决方案包括:
    • 使用Collections.synchronizedList(list)包装
    • 使用线程安全的替代类CopyOnWriteArrayList
  3. 避免频繁扩容:频繁扩容会导致性能下降,建议根据业务场景预估容量。
2.2.2 LinkedList:双向链表的实现
(1)底层原理

LinkedList基于双向链表 实现,每个节点(Node)包含三个部分:

  • prev:前驱指针,指向前一个节点
  • item:节点存储的元素
  • next:后继指针,指向后一个节点

JDK1.8 取消了循环链表设计,链表首尾节点的prevnext分别指向null。通过指针的指向关系,实现元素的存储与访问。

(2)核心方法与使用示例
java 复制代码
// 创建LinkedList集合
LinkedList<String> list = new LinkedList<>();

// 1. 首尾添加元素
list.addFirst("Head"); // 头部添加
list.addLast("Tail"); // 尾部添加
System.out.println(list); // 输出:[Head, Tail]

// 2. 首尾获取元素
String first = list.getFirst(); // 获取头部元素,返回"Head"
String last = list.getLast(); // 获取尾部元素,返回"Tail"

// 3. 首尾删除元素
list.removeFirst(); // 删除头部元素,返回"Head"
list.removeLast(); // 删除尾部元素,返回"Tail"
System.out.println(list); // 输出:[]

// 4. 作为队列使用(FIFO)
list.offer("A"); // 尾部添加(队列常用方法)
list.offer("B");
System.out.println(list.poll()); // 头部删除,返回"A"

// 5. 作为栈使用(LIFO)
list.push("C"); // 头部添加(栈常用方法)
list.push("D");
System.out.println(list.pop()); // 头部删除,返回"D"
(3)性能特点与适用场景
  • 优点:插入 / 删除效率高(定位节点后时间复杂度 O (1)),仅需修改相邻节点的指针;支持首尾快速操作,适合队列、栈等场景。
  • 缺点 :随机访问效率低(时间复杂度 O (n)),获取指定索引元素需从链表头或尾遍历;内存开销略高于ArrayList,每个元素需额外存储两个指针。
  • 适用场景写多读少的场景,例如频繁插入 / 删除的列表、队列(FIFO)、栈(LIFO)的实现、消息队列的临时存储等。
2.2.3 List 实现类对比
对比维度 ArrayList LinkedList Vector(已过时)
底层结构 动态数组 双向链表 动态数组
线程安全 是(方法加 synchronized)
随机访问效率 高(O (1)) 低(O (n)) 高(O (1)),但性能低
插入 / 删除效率 低(O (n)) 高(O (1),定位后) 低(O (n)),且性能低
扩容机制 1.5 倍扩容 无需扩容 2 倍扩容
内存开销 较低(仅存储元素) 较高(额外存储指针) 较高(可能浪费更多空间)
适用场景 读多写少 写多读少 老旧系统兼容

2.3 Set 接口:无序不可重复的集合

Set接口是Collection的另一子接口,核心特性是无序 (存储顺序与插入顺序不一致,LinkedHashSet 除外)、不可重复 (元素唯一)、无索引(不支持下标访问)。

2.3.1 HashSet:哈希表的实现
(1)底层原理

HashSet基于HashMap实现,内部持有一个HashMap实例,元素存储在HashMapKey中,Value固定为一个静态空对象(PRESENT)。其核心机制是通过哈希表保证元素唯一性:

  1. 计算元素的hashCode()值,确定元素在哈希表中的存储位置(哈希桶)。
  2. 若该位置无元素,直接存入;若有元素,通过equals()方法判断是否相同:
    • 相同:不存入(保证唯一性)
    • 不同:通过链表或红黑树解决哈希冲突(JDK1.8 后,链表长度超过 8 且数组容量≥64 时,链表转为红黑树)。
(2)核心方法与使用示例
java 复制代码
// 创建HashSet集合
Set<String> set = new HashSet<>();

// 1. 添加元素(重复元素不会被添加)
boolean add1 = set.add("Apple"); // 返回true
boolean add2 = set.add("Banana"); // 返回true
boolean add3 = set.add("Apple"); // 重复元素,返回false
System.out.println(set); // 输出:[Banana, Apple](无序)

// 2. 删除元素
boolean remove = set.remove("Banana"); // 返回true
System.out.println(set); // 输出:[Apple]

// 3. 判断元素是否存在
boolean contains = set.contains("Apple"); // 返回true

// 4. 遍历集合(无索引,仅支持增强for和迭代器)
// 方式1:增强for
for (String elem : set) {
    System.out.println(elem);
}

// 方式2:迭代器
Iterator<String> iterator = set.iterator();
while (iterator.hasNext()) {
    System.out.println(iterator.next());
}
(3)元素唯一性的保证

HashSet判断元素唯一性依赖两个方法:hashCode()equals(),必须同时满足以下规则:

  1. 若两个元素的hashCode()值不同 → 元素不同,直接存入不同哈希桶。
  2. 若两个元素的hashCode()值相同 → 需调用equals()判断:
    • equals()返回 true → 元素相同,不存入。
    • equals()返回 false → 元素不同,存入同一哈希桶(哈希冲突)。

因此,自定义对象存入HashSet时,必须重写hashCode()equals()方法,否则会导致重复元素无法被识别。

(4)性能特点与适用场景
  • 优点:增删查平均时间复杂度 O (1),效率极高;支持快速去重。
  • 缺点:元素无序,迭代顺序与插入顺序无关;不支持索引访问。
  • 适用场景:无需保留顺序的去重场景,例如存储用户 ID 集合、标签集合、不重复的配置项等。
2.3.2 TreeSet:红黑树的实现
(1)底层原理

TreeSet基于红黑树 (一种自平衡的二叉搜索树)实现,核心特性是元素有序 (自然排序或自定义排序)。其底层依赖TreeMap,元素存储在TreeMapKey中,通过红黑树的特性保证元素有序且唯一。

红黑树的特点是:

  • 左子树所有节点值 < 根节点值。
  • 右子树所有节点值 > 根节点值。
  • 任意节点的左右子树高度差不超过 1,保证查询效率为 O (log₂N)。
(2)核心方法与使用示例
java 复制代码
// 1. 自然排序(String实现了Comparable接口,按字典序排序)
Set<String> treeSet = new TreeSet<>();
treeSet.add("Banana");
treeSet.add("Apple");
treeSet.add("Cherry");
System.out.println(treeSet); // 输出:[Apple, Banana, Cherry]

// 2. 自定义排序(按字符串长度排序)
Set<String> customSet = new TreeSet<>((a, b) -> a.length() - b.length());
customSet.add("Java"); // 长度4
customSet.add("Go"); // 长度2
customSet.add("Python"); // 长度6
System.out.println(customSet); // 输出:[Go, Java, Python]

// 3. 有序相关方法
TreeSet<String> sortedSet = new TreeSet<>();
sortedSet.add("A");
sortedSet.add("B");
sortedSet.add("C");
System.out.println(sortedSet.first()); // 获取第一个元素,返回"A"
System.out.println(sortedSet.last()); // 获取最后一个元素,返回"C"
System.out.println(sortedSet.subSet("A", "C")); // 获取[A, C)区间元素,返回[A, B]
(3)排序规则的实现

TreeSet的有序性依赖两种方式:

  1. 自然排序 :元素实现Comparable接口,重写compareTo()方法。例如StringInteger等内置类已实现Comparable,可直接排序。
  2. 自定义排序 :创建TreeSet时传入Comparator接口实现类,定义排序逻辑。例如按字符串长度、自定义对象的某个字段排序。

若元素既未实现Comparable,也未指定Comparator,添加元素时会抛出ClassCastException

(4)性能特点与适用场景
  • 优点 :元素有序,支持范围查询(如subSet());增删查时间复杂度 O (log₂N),适合有序场景。
  • 缺点 :效率低于HashSet;元素必须支持排序(实现Comparable或指定Comparator)。
  • 适用场景:需要有序且去重的场景,例如按价格排序的商品集合、按时间排序的日志 ID 集合、范围查询的数据集等。
2.3.3 Set 实现类对比
对比维度 HashSet LinkedHashSet TreeSet
底层结构 哈希表(基于 HashMap) 哈希表 + 双向链表 红黑树(基于 TreeMap)
元素顺序 无序(哈希顺序) 有序(插入顺序) 有序(自然 / 自定义排序)
元素唯一性 依赖 hashCode ()+equals () 依赖 hashCode ()+equals () 依赖 compareTo ()/compare ()
增删查效率 O (1)(平均) O (1)(平均,略低于 HashSet) O(log₂N)
线程安全
支持 null 元素 是(仅一个) 是(仅一个)
适用场景 无序去重 有序去重(插入顺序) 有序去重(排序需求)

2.4 Queue 接口:队列结构的实现

Queue接口是Collection的子接口,遵循FIFO(先进先出) 原则,主要用于实现队列、双端队列等数据结构。

2.4.1 PriorityQueue:优先级队列
(1)底层原理

PriorityQueue基于 (完全二叉树)实现,核心特性是元素按优先级排序 ,出队时始终返回优先级最高的元素(而非插入顺序)。默认按 "自然排序"(升序),也可通过Comparator指定自定义优先级。

堆的特点是:

  • 小根堆(默认):父节点值 ≤ 子节点值,根节点是最小值。
  • 大根堆(自定义):父节点值 ≥ 子节点值,根节点是最大值。
(2)核心方法与使用示例
java 复制代码
// 1. 自然排序(Integer按升序,小根堆)
Queue<Integer> pq1 = new PriorityQueue<>();
pq1.offer(3);
pq1.offer(1);
pq1.offer(2);
System.out.println(pq1.poll()); // 出队最小值1
System.out.println(pq1.poll()); // 出队次小值2

// 2. 自定义排序(按降序,大根堆)
Queue<Integer> pq2 = new PriorityQueue<>((a, b) -> b - a);
pq2.offer(3);
pq2.offer(1);
pq2.offer(2);
System.out.println(pq2.poll()); // 出队最大值3
System.out.println(pq2.poll()); // 出队次大值2

// 3. 自定义对象排序(按年龄升序)
class Person {
    String name;
    int age;
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
    @Override
    public String toString() {
        return name + "(" + age + ")";
    }
}

Queue<Person> pq3 = new PriorityQueue<>((a, b) -> a.age - b.age);
pq3.offer(new Person("张三", 25));
pq3.offer(new Person("李四", 20));
pq3.offer(new Person("王五", 30));
System.out.println(pq3.poll()); // 输出:李四(20)
(3)性能特点与适用场景
  • 优点:优先级排序,出队始终返回最高优先级元素;入队 / 出队时间复杂度 O (log₂N)。
  • 缺点:元素无序(不按插入顺序);不支持 null 元素;非线程安全。
  • 适用场景:需要按优先级处理任务的场景,例如任务调度(高优先级任务先执行)、TOP-K 问题(取前 K 个最大值 / 最小值)、事件驱动的消息处理等。
2.4.2 ArrayDeque:双端队列
(1)底层原理

ArrayDeque基于循环数组 实现,支持双端操作(首尾均可添加 / 删除元素),可同时作为队列(FIFO)和栈(LIFO)使用。其核心优势是:

  • 循环数组:数组下标循环使用,避免元素移动,提高效率。
  • 动态扩容:容量不足时,扩容为 2 倍(若原容量 < 64)或 1.5 倍(若原容量≥64)。
(2)核心方法与使用示例
java 复制代码
// 创建ArrayDeque(双端队列)
Deque<String> deque = new ArrayDeque<>();

// 1. 作为队列使用(FIFO)
deque.offer("A"); // 尾部添加
deque.offer("B");
System.out.println(deque.poll()); // 头部删除,返回"A"

// 2. 作为栈使用(LIFO)
deque.push("C"); // 头部添加
deque.push("D");
System.out.println(deque.pop()); // 头部删除,返回"D"

// 3. 双端操作
deque.addFirst("Head"); // 头部添加
deque.addLast("Tail"); // 尾部添加
System.out.println(deque.getFirst()); // 获取头部,返回"Head"
System.out.println(deque.getLast()); // 获取尾部,返回"Tail"
deque.removeFirst(); // 删除头部
deque.removeLast(); // 删除尾部
(3)性能特点与适用场景
  • 优点 :首尾操作效率 O (1);支持队列与栈两种模式;比LinkedList更高效(数组比链表缓存命中率高)。
  • 缺点:不支持索引访问;非线程安全。
  • 适用场景:需要双端操作的场景,例如队列、栈的实现、滑动窗口算法、高频首尾操作的缓存等。

三、Map 体系:键值对映射的实现与实战

3.1 Map 接口:双列集合的根接口

Map接口是与Collection并列的核心接口,用于存储键值对(Key-Value) 映射关系。其核心特性是:

  • Key 唯一:同一个 Map 中,Key 不能重复(重复添加会覆盖 Value)。
  • Value 可重复:不同 Key 可对应相同 Value。
  • 无索引:不支持下标访问,需通过 Key 获取 Value。

Map接口的核心方法包括:

  • V put(K key, V value):添加键值对,返回旧 Value(无则返回 null)。
  • V get(Object key):通过 Key 获取 Value,无则返回 null。
  • V remove(Object key):通过 Key 删除键值对,返回删除的 Value。
  • Set<K> keySet():获取所有 Key 的集合。
  • Collection<V> values():获取所有 Value 的集合。
  • Set<Map.Entry<K, V>> entrySet():获取所有键值对的集合(推荐遍历方式)。

3.2 HashMap:哈希表的实现

HashMapMap接口最常用的实现类,基于哈希表(数组 + 链表 + 红黑树)实现,JDK1.8 对其进行了重大优化,解决了 JDK1.7 中链表过长导致的查询效率低下问题。

3.2.1 底层原理(JDK1.8+)

HashMap的底层结构是 "数组 + 链表 + 红黑树" 的组合,具体结构如下:

  1. 数组(哈希桶) :默认初始容量 16,存储链表或红黑树的头节点。数组下标通过 Key 的hashCode()计算得出:index = (容量-1) & hash
  2. 链表:当多个 Key 映射到同一哈希桶时,通过链表存储(哈希冲突解决)。当链表长度超过 8 且数组容量≥64 时,链表转为红黑树。
  3. 红黑树:当链表长度超过阈值(8)时转为红黑树,将查询时间复杂度从 O (n) 优化为 O (log₂N)。当红黑树节点数少于 6 时,转回链表。
3.2.2 核心参数与扩容机制

HashMap的性能与三个核心参数密切相关:

  1. 初始容量:默认 16,必须是 2 的幂(便于通过位运算计算下标)。
  2. 负载因子:默认 0.75,扩容阈值 = 容量 × 负载因子(例如 16×0.75=12)。当元素数量超过阈值时,触发扩容。
  3. 扩容机制:每次扩容为原容量的 2 倍,重新计算所有 Key 的哈希值与下标,将元素迁移到新数组。
3.2.3 核心方法与使用示例
java 复制代码
// 创建HashMap
Map<String, Integer> map = new HashMap<>();

// 1. 添加键值对(重复Key会覆盖Value)
map.put("Java", 90);
map.put("Python", 85);
map.put("Java", 95); // 覆盖原Value,返回90
System.out.println(map); // 输出:{Java=95, Python=85}

// 2. 获取Value
Integer javaScore = map.get("Java"); // 返回95
Integer goScore = map.get("Go"); // 无此Key,返回null

// 3. 删除键值对
Integer removedScore = map.remove("Python"); // 返回85
System.out.println(map); // 输出:{Java=95}

// 4. 遍历Map(三种方式)
// 方式1:遍历Key,再获取Value(效率低,需多次get())
Set<String> keys = map.keySet();
for (String key : keys) {
    System.out.println(key + ":" + map.get(key));
}

// 方式2:直接遍历Value(无法获取Key)
Collection<Integer> values = map.values();
for (Integer value : values) {
    System.out.println(value);
}

// 方式3:遍历键值对(推荐,效率高)
Set<Map.Entry<String, Integer>> entries = map.entrySet();
for (Map.Entry<String, Integer> entry : entries) {
    System.out.println(entry.getKey() + ":" + entry.getValue());
}
3.2.4 Key 的注意事项

HashMap的 Key 需满足与HashSet相同的规则:

  1. 重写hashCode()equals():保证 Key 的唯一性,避免哈希冲突导致的重复存储。
  2. Key 不可变:推荐使用不可变类(如 String、Integer)作为 Key,若 Key 是可变对象,修改后可能导致无法找到 Value。
3.2.5 性能特点与适用场景
  • 优点:增删查平均时间复杂度 O (1),效率极高;支持 null Key 和 null Value(仅一个 null Key)。
  • 缺点:Key 无序;非线程安全,多线程并发修改可能导致死循环(JDK1.7)或数据不一致(JDK1.8)。
  • 适用场景:无需 Key 有序、需要高效键值对映射的场景,例如缓存存储、配置映射、用户信息存储等。

3.3 TreeMap:红黑树的实现

TreeMap基于红黑树 实现,实现了SortedMap接口,核心特性是Key 有序(自然排序或自定义排序),适用于需要按 Key 排序的场景。

3.3.1 底层原理

TreeMap的底层红黑树与TreeSet一致,Key 的排序通过两种方式实现:

  1. 自然排序 :Key 实现Comparable接口,重写compareTo()方法(如 String、Integer)。
  2. 自定义排序 :创建TreeMap时传入Comparator接口,定义 Key 的排序逻辑。

红黑树的自平衡特性保证了 Key 的有序性,同时确保增删查时间复杂度为 O (log₂N)。

3.3.2 核心方法与使用示例
java 复制代码
// 1. 自然排序(String按字典序,Key有序)
Map<String, Integer> treeMap1 = new TreeMap<>();
treeMap1.put("Banana", 80);
treeMap1.put("Apple", 90);
treeMap1.put("Cherry", 85);
System.out.println(treeMap1); // 输出:{Apple=90, Banana=80, Cherry=85}

// 2. 自定义排序(Key按长度排序)
Map<String, Integer> treeMap2 = new TreeMap<>((a, b) -> a.length() - b.length());
treeMap2.put("Java", 95);
treeMap2.put("Go", 85);
treeMap2.put("Python", 90);
System.out.println(treeMap2); // 输出:{Go=85, Java=95, Python=90}

// 3. 有序相关方法
SortedMap<String, Integer> sortedMap = new TreeMap<>();
sortedMap.put("A", 1);
sortedMap.put("B", 2);
sortedMap.put("C", 3);
System.out.println(sortedMap.firstKey()); // 获取第一个Key,返回"A"
System.out.println(sortedMap.lastKey()); // 获取最后一个Key,返回"C"
System.out.println(sortedMap.subMap("A", "C")); // 获取[A, C)区间,返回{A=1, B=2}
3.3.3 性能特点与适用场景
  • 优点:Key 有序,支持范围查询;Key 唯一,基于排序保证唯一性;增删查时间复杂度 O (log₂N)。
  • 缺点 :效率低于HashMap;不支持 null Key;非线程安全。
  • 适用场景:需要 Key 有序的键值对场景,例如按日期排序的日志映射、按价格排序的商品库存、范围查询的统计数据等。

3.4 Map 实现类对比

对比维度 HashMap TreeMap LinkedHashMap Hashtable(已过时)
底层结构 数组 + 链表 + 红黑树 红黑树 哈希表 + 双向链表 哈希表
Key 顺序 无序 有序(自然 / 自定义) 有序(插入 / 访问顺序) 无序
线程安全 是(全表锁)
增删查效率 O (1)(平均) O(log₂N) O (1)(平均,略低于 HashMap) O (1)(平均,效率低)
支持 null Key/Value 均可(1 个 null Key) 均不支持 Key/Value 均可(1 个 null Key) 均不支持
适用场景 高效键值对映射 有序键值对映射 有序键值对(插入 / 访问顺序) 老旧系统兼容

3.5 线程安全的集合方案

上述集合类(ArrayList、HashMap 等)均为非线程安全,多线程环境下需通过以下方案保证线程安全:

3.5.1 Collections 工具类包装

Collections提供了synchronizedXXX()方法,为集合添加同步锁,例如:

java 复制代码
// 线程安全的List
List<String> safeList = Collections.synchronizedList(new ArrayList<>());

// 线程安全的Map
Map<String, Integer> safeMap = Collections.synchronizedMap(new HashMap<>());

缺点:采用 "全表锁",并发效率低,仅适用于低并发场景。

3.5.2 JUC 包中的线程安全集合

JUC(java.util.concurrent)包提供了专为高并发设计的集合类,核心包括:

  1. CopyOnWriteArrayList:基于 "写时复制"(COW)机制,读操作无锁,写操作复制数组,适用于读多写少场景。
  2. ConcurrentHashMap:JDK1.8 采用 "CAS+synchronized" 实现,对链表头节点加锁(细粒度锁),并发效率高,是 HashMap 的线程安全替代。
  3. CopyOnWriteArraySet:基于 CopyOnWriteArrayList 实现,线程安全的 Set。

示例(ConcurrentHashMap)

java 复制代码
// 创建线程安全的ConcurrentHashMap
Map<String, Integer> concurrentMap = new ConcurrentHashMap<>();

// 并发添加键值对
concurrentMap.put("Java", 90);
concurrentMap.put("Python", 85);

// 并发遍历(弱一致性)
for (Map.Entry<String, Integer> entry : concurrentMap.entrySet()) {
    System.out.println(entry.getKey() + ":" + entry.getValue());
}

四、算法复杂度:衡量算法效率的标尺

4.1 算法效率的衡量标准

在编程中,我们常说 "这个算法好" 或 "那个算法差",但如何量化这种 "好坏"?算法效率主要通过两个维度衡量:

  1. 时间复杂度:衡量算法的运行速度,即算法执行所耗费的时间与输入规模的关系。
  2. 空间复杂度:衡量算法所需的额外空间,即算法运行时临时占用的存储空间与输入规模的关系。

在计算机发展早期,存储空间有限,空间复杂度是重要考量;但随着硬件发展,如今更关注时间复杂度,空间复杂度通常作为次要指标。

4.2 时间复杂度

4.2.1 时间复杂度的定义

时间复杂度是一个数学函数,定量描述了算法的运行时间。它不计算具体的执行时间(因硬件、环境不同而变化),而是关注基本操作的执行次数与输入规模(通常用 N 表示)的增长趋势。

例如,计算 1+2+...+N 的算法:

java 复制代码
int sum = 0;
for (int i = 1; i <= N; i++) {
    sum += i; // 基本操作,执行N次
}

基本操作(sum += i)执行了 N 次,因此时间复杂度与 N 成正比。

4.2.2 大 O 渐进表示法

实际计算时间复杂度时,无需精确统计执行次数,只需关注 "增长趋势最显著的项",这就需要用到大 O 渐进表示法(Big O notation)。

(1)推导大 O 阶的三步法
  1. 用常数 1 取代所有加法常数:例如执行次数为 "2N+10",常数 10 替换为 1,变为 "2N+1"。
  2. 保留最高阶项:例如 "2N+1" 的最高阶项是 "2N",保留后为 "2N"。
  3. 去除最高阶项的系数:例如 "2N" 去除系数 2,最终为 "O (N)"。
(2)示例解析

以如下代码为例,分析其时间复杂度:

java 复制代码
void func1(int N) {
    int count = 0;
    // 嵌套循环:执行N*N次
    for (int i = 0; i < N; i++) {
        for (int j = 0; j < N; j++) {
            count++;
        }
    }
    // 单层循环:执行2*N次
    for (int k = 0; k < 2 * N; k++) {
        count++;
    }
    // 循环:执行10次
    int M = 10;
    while ((M--) > 0) {
        count++;
    }
    System.out.println(count);
}
  • 基本操作总次数:F (N) = N² + 2N + 10
  • 用大 O 表示法推导:
    1. 常数 10→1:F (N) = N² + 2N + 1
    2. 保留最高阶项 N²:F (N) = N²
    3. 去除系数:O (N²)
  • 最终时间复杂度:O (N²)
4.2.3 常见时间复杂度及示例

时间复杂度的增长趋势从低到高依次为:O (1) < O (logN) < O (N) < O (NlogN) < O (N²) < O (2ⁿ) < O (N!),以下是常见复杂度的示例:

(1)O (1):常数阶

基本操作执行次数与输入规模无关,始终为常数次。

java 复制代码
void func(int N) {
    int a = 1;
    int b = 2;
    int sum = a + b; // 仅执行1次
    System.out.println(sum);
}
(2)O (logN):对数阶

基本操作执行次数与输入规模 N 的对数成正比,常见于 "每次排除一半数据" 的场景(如二分查找)。

java 复制代码
// 二分查找:在有序数组中查找目标值
int binarySearch(int[] array, int target) {
    int left = 0;
    int right = array.length - 1;
    while (left <= right) {
        int mid = (left + right) / 2;
        if (array[mid] == target) {
            return mid;
        } else if (array[mid] < target) {
            left = mid + 1;
        } else {
            right = mid - 1;
        }
    }
    return -1;
}

每次循环排除一半元素,执行次数为 log₂N,时间复杂度为 O (logN)(算法分析中 log 默认以 2 为底)。

(3)O (N):线性阶

基本操作执行次数与输入规模 N 成正比,常见于单层循环。

java 复制代码
void func(int N) {
    int count = 0;
    for (int i = 0; i < N; i++) {
        count++; // 执行N次
    }
    System.out.println(count);
}
(4)O (NlogN):线性对数阶

基本操作执行次数为 N×logN,常见于 "外层循环 N 次,内层循环 logN 次" 的场景(如快速排序、归并排序)。

java 复制代码
// 示例:外层循环N次,内层循环logN次
void func(int N) {
    for (int i = 0; i < N; i++) {
        // 内层二分查找:O(logN)
        int left = 0;
        int right = N - 1;
        while (left <= right) {
            int mid = (left + right) / 2;
            if (mid == i) {
                break;
            } else if (mid < i) {
                left = mid + 1;
            } else {
                right = mid - 1;
            }
        }
    }
}
(5)O (N²):平方阶

基本操作执行次数与输入规模 N 的平方成正比,常见于嵌套循环(外层 N 次,内层 N 次)。

java 复制代码
// 冒泡排序:外层循环N次,内层循环N次
void bubbleSort(int[] array) {
    for (int i = 0; i < array.length; i++) {
        for (int j = 0; j < array.length - i - 1; j++) {
            if (array[j] > array[j + 1]) {
                int temp = array[j];
                array[j] = array[j + 1];
                array[j + 1] = temp;
            }
        }
    }
}
(6)O (2ⁿ):指数阶

基本操作执行次数与 2 的 N 次方成正比,常见于递归场景(如未优化的斐波那契数列),效率极低,应避免。

java 复制代码
// 斐波那契数列(递归):执行次数为2ⁿ
int fibonacci(int N) {
    return N < 2 ? N : fibonacci(N - 1) + fibonacci(N - 2);
}
4.2.4 最好、平均、最坏情况

部分算法的时间复杂度存在三种情况:

  • 最好情况:任意输入规模的最小运行次数(下界)。例如在数组中查找元素,最好情况是 1 次找到。
  • 平均情况:任意输入规模的期望运行次数。例如在数组中查找元素,平均情况是 N/2 次找到。
  • 最坏情况:任意输入规模的最大运行次数(上界)。例如在数组中查找元素,最坏情况是 N 次找到。

在实际分析中,通常关注最坏情况,因为它代表了算法的 "最差性能",是系统设计的重要依据。例如数组查找的时间复杂度按最坏情况定为 O (N)。

4.3 空间复杂度

4.3.1 空间复杂度的定义

空间复杂度是对算法运行过程中临时占用存储空间大小的量度,同样使用大 O 渐进表示法。它不计算具体的字节数,而是统计 "额外变量的个数" 与输入规模的关系。

4.3.2 常见空间复杂度及示例
(1)O (1):常数阶

算法所需额外空间与输入规模无关,始终为常数个变量。

java 复制代码
// 交换两个变量:仅使用1个临时变量temp
void swap(int a, int b) {
    int temp = a; // 额外变量,常数个
    a = b;
    b = temp;
}
(2)O (N):线性阶

算法所需额外空间与输入规模 N 成正比,常见于动态数组、递归栈帧等场景。

java 复制代码
// 动态数组:额外空间为N
int[] fibonacci(int N) {
    long[] fibArray = new long[N + 1]; // 额外空间为N+1,O(N)
    fibArray[0] = 0;
    fibArray[1] = 1;
    for (int i = 2; i <= N; i++) {
        fibArray[i] = fibArray[i - 1] + fibArray[i - 2];
    }
    return fibArray;
}
(3)O (logN):对数阶

算法所需额外空间与输入规模 N 的对数成正比,常见于递归二分查找(递归栈帧为 logN 层)。

java 复制代码
// 递归二分查找:递归栈帧为logN层,空间复杂度O(logN)
int binarySearchRecursive(int[] array, int left, int right, int target) {
    if (left > right) {
        return -1;
    }
    int mid = (left + right) / 2;
    if (array[mid] == target) {
        return mid;
    } else if (array[mid] < target) {
        return binarySearchRecursive(array, mid + 1, right, target);
    } else {
        return binarySearchRecursive(array, left, mid - 1, target);
    }
}
(4)O (N²):平方阶

算法所需额外空间与输入规模 N 的平方成正比,常见于二维数组场景(如矩阵)。

java 复制代码
// 创建N×N的二维数组:额外空间为N²
int[][] createMatrix(int N) {
    int[][] matrix = new int[N][N]; // 额外空间为N×N,O(N²)
    for (int i = 0; i < N; i++) {
        for (int j = 0; j < N; j++) {
            matrix[i][j] = i * N + j;
        }
    }
    return matrix;
}
4.3.3 空间复杂度与时间复杂度的区别
  • 时间复杂度:关注 "执行次数",是 "运行时间" 的衡量。
  • 空间复杂度:关注 "额外变量个数",是 "存储空间" 的衡量。
  • 权衡关系 :部分算法可通过 "空间换时间" 或 "时间换空间" 优化。例如:
    • 缓存机制:用额外空间存储高频访问数据,减少计算时间(空间换时间)。
    • 压缩算法:用额外计算时间减少存储空间(时间换空间)。

4.4 复杂度分析实战示例

4.4.1 示例 1:冒泡排序的复杂度分析
java 复制代码
void bubbleSort(int[] array) {
    for (int end = array.length; end > 0; end--) {
        boolean sorted = true;
        for (int i = 1; i < end; i++) {
            if (array[i - 1] > array[i]) {
                int temp = array[i - 1];
                array[i - 1] = array[i];
                array[i] = temp;
                sorted = false;
            }
        }
        if (sorted) {
            break;
        }
    }
}
  • 时间复杂度
    • 最好情况:数组已有序,仅执行外层循环 1 次,内层循环 N 次 → O (N)。
    • 最坏情况:数组逆序,外层循环 N 次,内层循环 N 次 → O (N²)。
    • 实际分析:按最坏情况定为 O (N²)。
  • 空间复杂度:仅使用 1 个临时变量 temp 和 1 个布尔变量 sorted → O (1)。
4.4.2 示例 2:递归阶乘的复杂度分析
java 复制代码
long factorial(int N) {
    return N < 2 ? N : factorial(N - 1) * N;
}
  • 时间复杂度:递归调用 N 次,每次调用执行 1 次乘法 → O (N)。
  • 空间复杂度:递归栈帧为 N 层,每层使用常数个变量 → O (N)。
4.4.3 示例 3:斐波那契数列(递归)的复杂度分析
java 复制代码
int fibonacci(int N) {
    return N < 2 ? N : fibonacci(N - 1) + fibonacci(N - 2);
}
  • 时间复杂度:递归调用次数为 2ⁿ(递归树为满二叉树,节点数为 2ⁿ-1) → O (2ⁿ)。
  • 空间复杂度:递归栈帧最大深度为 N(最坏情况) → O (N)。

五、总结与实战建议

5.1 集合框架选择指南

根据业务场景选择合适的集合类,是提升程序性能的关键,核心选择逻辑如下:

  1. 需存储单列数据

    • 有序可重复、频繁查询 → ArrayList。
    • 有序可重复、频繁增删 → LinkedList。
    • 无序不可重复、高效查询 → HashSet。
    • 有序不可重复、需排序 → TreeSet。
    • 队列 / 栈场景 → ArrayDeque。
    • 优先级任务 → PriorityQueue。
  2. 需存储键值对数据

    • 高效查询、无需排序 → HashMap。
    • 需 Key 有序 → TreeMap。
    • 需保留插入 / 访问顺序 → LinkedHashMap。
    • 高并发场景 → ConcurrentHashMap。
  3. 线程安全需求

    • 低并发 → Collections.synchronizedXXX ()。
    • 高并发 → CopyOnWriteArrayList、ConcurrentHashMap。

5.2 算法复杂度优化建议

  1. 时间复杂度优化

    • 避免嵌套循环(O (N²)),尽量用 "空间换时间"(如用哈希表将 O (N²) 优化为 O (N))。
    • 递归场景避免重复计算(如斐波那契数列用动态规划优化为 O (N))。
    • 大数据量查询优先用二分查找(O (logN))而非线性查找(O (N))。
  2. 空间复杂度优化

    • 避免不必要的动态数组 / 集合,尽量复用变量。
    • 递归深度过大时,用迭代替代递归(减少栈帧空间)。
    • 大型数据优先用流式处理,避免一次性加载到内存。

5.3 学习路径建议

  1. 基础阶段:掌握集合框架的 API 使用,能熟练创建、操作集合。
  2. 进阶阶段:理解底层数据结构(数组、链表、哈希表、红黑树),能分析集合的性能特点。
  3. 实战阶段:通过 LeetCode、牛客网刷题,将集合框架与算法复杂度结合,解决实际问题(如 Top-K、哈希冲突处理)。
  4. 深入阶段:阅读 JDK 源码(如 HashMap、ArrayList),理解底层实现细节(如扩容机制、红黑树旋转)。

通过以上学习,不仅能熟练使用集合框架,更能在实际开发中做出 "性能最优" 的技术选型,同时应对面试中的深度问题,为成为资深 Java 开发者奠定基础。


总结

以上就是今天要讲的内容,本文简单记录了java数据结构,仅作为一份简单的笔记使用,大家根据注释理解,您的点赞关注收藏就是对小编最大的鼓励!

相关推荐
DN20202 小时前
AI销售机器人:节日祝福转化率提升30倍
人工智能·python·深度学习·机器学习·机器人·节日
win x2 小时前
JavaSE(基础)高频面试点及 知识点
java·面试·职场和发展
Terio_my2 小时前
简要 Java 面试题学习
java·开发语言·学习
爱喝可乐的老王2 小时前
PyTorch简介与安装
人工智能·pytorch·python
看我干嘛!2 小时前
第三次python作业
服务器·数据库·python
deephub2 小时前
用 PyTorch 实现 LLM-JEPA:不预测 token,预测嵌入
人工智能·pytorch·python·深度学习·大语言模型
好好研究3 小时前
Spring Boot - Thymeleaf模板引擎
java·spring boot·后端·thymeleaf
爬山算法3 小时前
Hibernate(76)如何在混合持久化环境中使用Hibernate?
java·后端·hibernate
编程彩机3 小时前
互联网大厂Java面试:从分布式缓存到消息队列的技术场景解析
java·redis·面试·kafka·消息队列·微服务架构·分布式缓存