.NET 数据结构
.NET 自带数据结构底层实现详解
一、线性数据结构
1.1 List - 动态数组
底层实现: 可变大小的数组
核心字段:
internal T[] _items; // 存储元素的数组
internal int _size; // 当前元素数量
internal int _version; // 版本号,用于检测集合修改
private const int DefaultCapacity = 4; // 默认初始容量
关键特性:
- 存储方式: 连续内存的动态数组
- 扩容策略: 容量不足时,新容量 = Math.Max(当前容量 * 2, 当前容量 + 4)
- 时间复杂度:
- 索引访问:O(1)
- 末尾添加:平摊O(1),最坏O(n)
- 中间插入/删除:O(n)
- 空间效率: 高,元素紧密排列
1.2 Queue - 队列
底层实现: 循环数组(环形缓冲区)
核心字段:
private T[] _array; // 存储元素的数组
private int _head; // 队头索引
private int _tail; // 队尾索引
private int _size; // 元素数量
private int _version; // 版本号
关键特性:
- 存储方式: 循环数组,避免元素移动
- 操作机制:
- Enqueue:在_tail位置添加,_tail = (_tail + 1) % _array.Length
- Dequeue:从_head位置取出,_head = (_head + 1) % _array.Length
- 扩容策略: 容量不足时创建新数组,重新排列元素
- 时间复杂度: Enqueue和Dequeue均为O(1)
1.3 Stack - 栈
底层实现: 动态数组
核心字段:
private T[] _array; // 存储元素的数组
private int _size; // 元素数量(同时也是栈顶指针)
private int _version; // 版本号
private const int DefaultCapacity = 4;
关键特性:
- 存储方式: 数组,_size指向下一个可用位置
- 操作机制:
- Push:_array[_size++] = item
- Pop:return _array[--_size]
- 扩容策略: 与List
类似,容量翻倍 - 时间复杂度: Push和Pop均为O(1)
二、链表结构
2.1 LinkedList - 双向循环链表
底层实现: 双向循环链表
核心字段:
internal LinkedListNode<T>? head; // 头节点
internal int count; // 节点数量
internal int version; // 版本号
节点结构:
public sealed class LinkedListNode<T>
{
internal LinkedList<T>? list; // 所属链表
internal LinkedListNode<T>? next; // 下一个节点
internal LinkedListNode<T>? prev; // 前一个节点
internal T item; // 存储的值
}
关键特性:
- 存储方式: 每个节点包含prev/next指针,形成双向循环链表
- 循环结构: head.prev指向尾节点,尾节点.next指向head
- 时间复杂度:
- 头尾插入/删除:O(1)
- 中间插入/删除(已知节点):O(1)
- 查找:O(n)
三、哈希表结构
3.1 Dictionary<TKey, TValue> - 哈希表
底层实现: 数组 + 链地址法解决冲突
核心字段:
private int[]? _buckets; // 哈希桶数组
private Entry[]? _entries; // 存储键值对的数组
private int _count; // 元素数量
private int _freeList; // 空闲链表头
private IEqualityComparer<TKey>? _comparer; // 键比较器
Entry结构:
private struct Entry
{
public uint hashCode; // 哈希值
public int next; // 下一个节点索引(链表结构)
public TKey key; // 键
public TValue value; // 值
}
关键特性:
- 冲突解决: 链地址法,相同哈希位置的元素通过链表连接
- 扩容机制: 负载因子达到1.0时扩容,新容量为大于当前容量2倍的最小质数
- 时间复杂度: 平均O(1),最坏O(n)
3.2 HashSet - 哈希集合
底层实现: 与Dictionary<TKey, TValue>相同的哈希表结构
核心特性:
- 复用Dictionary实现: 只存储键,值部分忽略
- 去重机制: 基于哈希值和Equals比较
- 时间复杂度: 与Dictionary相同
3.3 ConcurrentDictionary<TKey, TValue> - 并发哈希表
底层实现: 分段锁 + 哈希表
核心结构:
private volatile Tables _tables; // 内部表结构
private sealed class Tables
{
internal readonly VolatileNode[] _buckets; // 哈希桶
internal readonly object[] _locks; // 分段锁数组
internal readonly int[] _countPerLock; // 每段的元素计数
internal readonly IEqualityComparer<TKey>? _comparer;
}
关键特性:
- 并发策略: 分段锁,将哈希表分为多个段,每段独立加锁
- 锁粒度: 默认锁数量等于CPU核心数,最大1024个锁
- 读操作: 大部分情况下无锁(volatile读取)
- 写操作: 只锁定对应段,提高并发性能
四、树型结构
4.1 SortedSet - 红黑树
底层实现: 红黑树(自平衡二叉搜索树)
核心字段:
private Node? root; // 根节点
private IComparer<T> comparer; // 比较器
private int count; // 节点数量
节点结构:
internal sealed class Node
{
internal T Item; // 存储的值
internal Node? Left; // 左子节点
internal Node? Right; // 右子节点
internal NodeColor Color; // 节点颜色(红/黑)
internal bool IsRed => Color == NodeColor.Red;
}
关键特性:
- 平衡策略: 红黑树性质保证树的平衡
- 有序性: 中序遍历得到有序序列
- 时间复杂度: 插入、删除、查找均为O(log n)
4.2 SortedDictionary<TKey, TValue> - 基于红黑树的字典
底层实现: 内部使用TreeSet<KeyValuePair<TKey, TValue>>
核心字段:
private readonly TreeSet<KeyValuePair<TKey, TValue>> _set;
关键特性:
- 复用SortedSet: 将键值对作为整体存储在红黑树中
- 排序依据: 基于键的比较器排序
- 时间复杂度: 与SortedSet相同,O(log n)
五、特殊数据结构
5.1 StringBuilder - 可变字符串
底层实现: 链式的字符数组块
核心字段:
internal char[] m_ChunkChars; // 当前块的字符数组
internal StringBuilder? m_ChunkPrevious; // 前一个块
internal int m_ChunkLength; // 当前块的字符数
internal int m_ChunkOffset; // 当前块在整个字符串中的偏移
关键特性:
- 分块存储: 每个块默认16个字符,避免频繁数组复制
- 链式结构: 多个块通过指针连接
- 扩容策略: 块满时创建新块,而非整体扩容
5.2 Array - 固定大小数组
底层实现: 连续内存块
关键特性:
- 内存布局: 元素在内存中连续存储
- 类型安全: 编译时类型检查
- 多维支持: 支持多维数组和交错数组
- 性能: 最佳的缓存局部性和访问性能
六、性能对比总结
| 数据结构 | 查找 | 插入 | 删除 | 空间复杂度 | 主要特点 |
|---|---|---|---|---|---|
| List |
O(1)索引 O(n)值 |
O(1)末尾 O(n)中间 |
O(n) | O(n) | 随机访问,缓存友好 |
| LinkedList |
O(n) | O(1)已知位置 | O(1)已知位置 | O(n) | 插入删除高效 |
| Dictionary<TKey,TValue> | O(1)平均 | O(1)平均 | O(1)平均 | O(n) | 快速键值查找 |
| HashSet |
O(1)平均 | O(1)平均 | O(1)平均 | O(n) | 去重,集合运算 |
| SortedSet |
O(log n) | O(log n) | O(log n) | O(n) | 自动排序,范围查询 |
| SortedDictionary<TKey,TValue> | O(log n) | O(log n) | O(log n) | O(n) | 有序键值对 |
| Queue |
- | O(1) | O(1) | O(n) | FIFO |
| Stack |
- | O(1) | O(1) | O(n) | LIFO |
| ConcurrentDictionary<TKey,TValue> | O(1)平均 | O(1)平均 | O(1)平均 | O(n) | 线程安全 |
七、选择建议
- 频繁随机访问:选择Array或List
- 频繁插入删除(中间位置):选择LinkedList
- 键值查找:选择Dictionary<TKey,TValue>
- 去重和集合运算:选择HashSet
- 需要排序:选择SortedSet
或SortedDictionary<TKey,TValue> - 队列操作:选择Queue
- 栈操作:选择Stack
- 多线程环境:选择ConcurrentDictionary<TKey,TValue>或其他Concurrent集合
- 字符串拼接:选择StringBuilder
这些数据结构的设计充分考虑了性能、内存使用和特定场景的需求,是.NET框架高性能的重要基础。
本文来自博客园,作者:MadLongTom,转载请注明原文链接:https://www.cnblogs.com/madtom/p/19044693
浙公网安备 33010602011771号