Java 中所有数据结构完整讲解
先分两大块:
- 底层物理数据结构(计算机基础:数组、链表、栈、队列、哈希表、树、跳表)
- Java 集合封装好的数据结构(基于上面底层封装,开发直接用:List/Set/Queue/Map)
一、基础物理底层数据结构(所有集合的根基)
1. 数组 Array
特点
- 一段连续内存,下标随机访问 O (1);
- 长度固定,不能自动扩容;
- 中间插入 / 删除需要整体移位,效率低 O (n);
- 支持重复元素、有序。
通俗例子
电影院一排连续座位,座位号就是下标,找座位直接报号很快;但中间有人离场,后面所有人要往前挪。
java
运行
public class ArrayDemo {
public static void main(String[] args) {
// 固定长度数组,只能存3个
int[] arr = new int[3];
arr[0] = 10;
arr[1] = 20;
arr[2] = 30;
// 随机访问极快
System.out.println(arr[1]); // 20
// 缺点:长度固定,存第4个直接报错
// arr[3] = 40; // ArrayIndexOutOfBoundsException
}
}
2. 链表 LinkedList(单向 / 双向链表)
特点
- 内存不连续,每个节点存「数据 + 下一个节点地址」;
- 无固定长度,随时新增删除;
- 头尾增删 O (1),根据下标查找 O (n);
- 双向链表可向前、向后遍历。
通俗例子
一串珠子,每颗珠子连着下一颗,拿掉中间一颗只需要改前后两颗的连线,不用移动整串;但想找到第 5 颗珠子必须从头一颗一颗数。
java
运行
// 手写单向链表节点
class Node {
int val;
Node next;
Node(int val) { this.val = val; }
}
public class LinkDemo {
public static void main(String[] args) {
// 1 -> 2 -> 3
Node n1 = new Node(1);
Node n2 = new Node(2);
Node n3 = new Node(3);
n1.next = n2;
n2.next = n3;
// 遍历链表,必须从头开始
Node cur = n1;
while(cur != null){
System.out.print(cur.val + " ");
cur = cur.next;
}
}
}
3. 栈 Stack(后进先出 LIFO)
特点
只允许一端存、一端取,最后放进去的最先拿出来。 操作:push 入栈、pop 出栈、peek 查看栈顶。
通俗例子
快递箱子堆叠:最后放上去的箱子,要先拿出来才能碰下面的;浏览器前进后退、函数调用栈。
java
运行
import java.util.ArrayDeque;
public class StackDemo {
public static void main(String[] args) {
// 官方推荐ArrayDeque代替老旧Stack类
ArrayDeque<String> stack = new ArrayDeque<>();
stack.push("页面1");
stack.push("页面2");
stack.push("页面3");
System.out.println("当前栈顶:" + stack.peek()); // 页面3
System.out.println("取出:" + stack.pop()); // 页面3
System.out.println("取出:" + stack.pop()); // 页面2
}
}
4. 队列 Queue(先进先出 FIFO)
特点
一端存(队尾)、一端取(队头),先放进去的先消费。
通俗例子
排队打饭,先来的人先打饭;消息队列、线程池任务排队。
java
运行
import java.util.ArrayDeque;
public class QueueDemo {
public static void main(String[] args) {
ArrayDeque<String> queue = new ArrayDeque<>();
queue.offer("顾客A");
queue.offer("顾客B");
queue.offer("顾客C");
System.out.println(queue.poll()); // 顾客A 先排队先走
System.out.println(queue.poll()); // 顾客B
}
}
5. 哈希表 Hash Table
特点
底层 = 数组 + 链表 / 红黑树; 通过哈希函数把 key 换算成数组下标,读写接近 O (1); 存在哈希碰撞(多个 key 算出同一个下标,用链表挂载)。
通俗例子
储物柜:每个钥匙 (key) 对应一个柜子编号 (哈希值),直接找到柜子;两把钥匙分到同一个柜子就放一层链表。 Java 中 HashMap、HashSet 底层都是哈希表。
6. 树 Tree(重点:二叉树、二叉搜索树、红黑树)
6.1 普通二叉树
每个节点最多两个子节点(左、右)。
6.2 二叉搜索树 BST
左子树全部 <当前节点,右子树全部> 当前节点;中序遍历有序。 缺陷:极端情况退化成链表,查询变慢。
6.3 红黑树(平衡二叉搜索树)
自平衡,左右子树高度差不会太大,查询 / 插入 / 删除稳定 O (logn); TreeMap、TreeSet、HashMap 扩容后链表过长都会转为红黑树。
通俗例子
有序字典,二分查找,找中间值快速缩小范围。
7. 跳表 SkipList
多层有序链表,相当于给普通链表建立多层 “索引”; 查询效率媲美红黑树,插入删除更简单,无锁并发友好; 对应集合:ConcurrentSkipListMap / ConcurrentSkipListSet。
二、Java 封装好的集合数据结构(开发直接使用)
分两大类:Collection 单列集合(存单个对象)、Map 双列集合(键值对)
(一)List 列表:有序、可重复、带索引
底层:动态数组 / 双向链表
1. ArrayList 动态数组
底层:Object 数组,自动 1.5 倍扩容 适用:查询多、遍历多、尾部新增,极少中间删插
java
运行
import java.util.ArrayList;
public class ArrayListTest {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
list.add("苹果");
list.add("香蕉");
list.add("橘子");
// 根据下标快速查询
System.out.println(list.get(0));
// 尾部添加效率极高
list.add("葡萄");
System.out.println(list);
}
}
2. LinkedList 双向链表
底层:双向链表 适用:频繁头部 / 尾部增删,很少按下标查询;可做队列、栈
java
运行
import java.util.LinkedList;
public class LinkedListTest {
public static void main(String[] args) {
LinkedList<Integer> list = new LinkedList<>();
list.addFirst(1); // 头部插入快
list.addLast(99); // 尾部插入快
System.out.println(list);
}
}
3. CopyOnWriteArrayList 写时复制数组
底层:数组,修改时复制新数组,读不加锁,线程安全 适用:并发场景,读多写极少(系统白名单、常量配置)
java
运行
import java.util.concurrent.CopyOnWriteArrayList;
public class CopyListTest {
public static void main(String[] args) {
CopyOnWriteArrayList<String> whiteIp = new CopyOnWriteArrayList<>();
whiteIp.add("127.0.0.1");
// 多线程并发读取无并发修改异常
new Thread(() -> System.out.println(whiteIp.contains("127.0.0.1"))).start();
}
}
4. Vector(过时)
同步动态数组,所有方法加synchronized,性能差,新项目禁用。
(二)Set 集:不可重复,无索引
依靠 hashCode+equals 去重
1. HashSet 哈希表(无序)
底层:HashMap 哈希表,存取顺序不一致,允许 1 个 null 场景:批量数据去重、判断元素是否存在
java
运行
import java.util.HashSet;
public class HashSetTest {
public static void main(String[] args) {
HashSet<Integer> set = new HashSet<>();
set.add(10);
set.add(20);
set.add(10); // 重复自动丢弃
System.out.println(set); // [10,20] 无序
}
}
2. LinkedHashSet 哈希表 + 双向链表(插入有序)
场景:去重同时保留插入顺序
java
运行
import java.util.LinkedHashSet;
public class LinkedHashSetTest {
public static void main(String[] args) {
LinkedHashSet<String> log = new LinkedHashSet<>();
log.add("登录");
log.add("下单");
log.add("登录");
System.out.println(log); // [登录, 下单] 和插入顺序一致
}
}
3. TreeSet 红黑树(自动排序)
场景:自动排序、区间筛选、排行榜
java
运行
import java.util.TreeSet;
public class TreeSetTest {
public static void main(String[] args) {
TreeSet<Integer> score = new TreeSet<>();
score.add(85);
score.add(60);
score.add(95);
System.out.println(score); // [60,85,95] 自动升序
System.out.println(score.last()); // 最高分95
}
}
4. ConcurrentSkipListSet 跳表(并发有序 Set)
多线程环境需要有序去重数据时使用。
(三)Queue / Deque 队列、双端队列
1. ArrayDeque 循环数组(栈 / 普通队列首选)
性能比 LinkedList 高,不允许 null
java
运行
import java.util.ArrayDeque;
public class ArrayDequeTest {
public static void main(String[] args) {
// 队列用法
ArrayDeque<String> queue = new ArrayDeque<>();
queue.offer("任务1");
queue.offer("任务2");
System.out.println(queue.poll()); // 任务1
// 栈用法
ArrayDeque<String> stack = new ArrayDeque<>();
stack.push("首页");
stack.push("详情页");
System.out.println(stack.pop()); // 详情页
}
}
2. PriorityQueue 优先队列(最小堆)
不是 FIFO,按自定义优先级取出,用于 TopK、任务优先级调度
java
运行
import java.util.PriorityQueue;
public class PriorityQueueTest {
public static void main(String[] args) {
// 默认小顶堆,数字小优先取出
PriorityQueue<Integer> pq = new PriorityQueue<>();
pq.add(5);
pq.add(1);
pq.add(3);
while (!pq.isEmpty()) {
System.out.print(pq.poll() + " "); // 1 3 5
}
}
}
3. 并发阻塞队列(多线程生产者消费者)
- ArrayBlockingQueue:有界固定长度阻塞队列
- LinkedBlockingQueue:无界 / 有界链表阻塞队列(线程池默认)
- DelayQueue:延迟队列(订单超时、定时清理)
- ConcurrentLinkedQueue:无锁 CAS 高并发非阻塞队列
(四)Map 键值对存储,Key 唯一
1. HashMap 哈希表(单线程首选,无序)
key 允许 1 个 null,读写 O (1),非线程安全
java
运行
import java.util.HashMap;
public class HashMapTest {
public static void main(String[] args) {
HashMap<Long, String> userMap = new HashMap<>();
userMap.put(1001L, "小明");
userMap.put(1002L, "小红");
System.out.println(userMap.get(1001L)); // 小明
}
}
2. LinkedHashMap 哈希 + 双向链表(插入有序 / LRU 缓存)
java
运行
import java.util.LinkedHashMap;
import java.util.Map;
public class LRUCacheTest {
public static void main(String[] args) {
// LRU缓存,最多存2个,淘汰最久未访问
Map<String, String> cache = new LinkedHashMap<>(2, 0.75f, true) {
@Override
protected boolean removeEldestEntry(Map.Entry eldest) {
return size() > 2;
}
};
cache.put("k1", "v1");
cache.put("k2", "v2");
cache.get("k1"); // k1变为最新访问
cache.put("k3", "v3"); // 淘汰k2
System.out.println(cache); // {k1=v1, k3=v3}
}
}
3. TreeMap 红黑树(key 自动排序)
支持区间查询,key 不能为 null
java
运行
import java.util.TreeMap;
public class TreeMapTest {
public static void main(String[] args) {
TreeMap<Integer, String> levelMap = new TreeMap<>();
levelMap.put(80, "良好");
levelMap.put(60, "及格");
levelMap.put(90, "优秀");
System.out.println(levelMap); // 按键升序
}
}
4. ConcurrentHashMap 并发哈希表(多线程 Map 首选)
线程安全,性能远超老旧 Hashtable,key/value 不允许 null
java
运行
import java.util.concurrent.ConcurrentHashMap;
public class ConcurrentMapTest {
public static void main(String[] args) throws InterruptedException {
ConcurrentHashMap<Integer, String> onlineUser = new ConcurrentHashMap<>();
// 多线程并发写入不会报错
new Thread(() -> onlineUser.put(1, "用户1")).start();
new Thread(() -> onlineUser.put(2, "用户2")).start();
Thread.sleep(100);
System.out.println(onlineUser);
}
}
5. ConcurrentSkipListMap 跳表(高并发有序 Map)
多线程场景需要有序键值映射使用。
6. Properties 配置文件专用 Map
继承 Hashtable,只存字符串,读取.properties配置文件。
三、所有数据结构选型极简总结
- 简单有序列表、查询多 → ArrayList
- 频繁头尾增删、队列栈 → ArrayDeque / LinkedList
- 去重无序 → HashSet;去重有序 → LinkedHashSet;去重排序 → TreeSet
- 普通键值缓存单线程 → HashMap;有序键值 → LinkedHashMap;排序键值 → TreeMap
- 多线程读写 Map → ConcurrentHashMap
- 任务按优先级执行 → PriorityQueue
- 多线程排队生产消费 → ArrayBlockingQueue
- 延迟过期任务 → DelayQueue
- 并发读多写少列表 → CopyOnWriteArrayList
- 高并发且需要有序集合 → ConcurrentSkipListSet / ConcurrentSkipListMap
更多推荐



所有评论(0)