在计算机科学中，**关联数组**（英語：），又称**映射**（）、**字典**（）是一个抽象的数据结构，它包含着类似于（键，值）的有序对。一个关联数组中的有序对可以重复（如C++中的multimap）也可以不重复（如C++中的map）。

这种数据结构包含以下几种常见的操作：

* 向关联数组添加配对
* 从关联数组内删除配对
* 修改关联数组内的配对
* 根据已知的键寻找配对

**字典问题**是设计一种能够具备关联数组特性的数据结构。解决字典问题的常用方法，是利用散列表或搜索树。有些情况下，也可以使用直接寻址的数组、二叉查找树或其他专门的结构。

关联数组有许多应用，包括诸如记忆化和修饰模式的编程模式。

许多程序设计语言内置基本的数据类型，提供对关联数组的支持。而内容定址存储器（英语：）则是硬件层面上实现对关联数组的支持。

操作
--

关联数组中，键与值的关联通常称作"映射"，"映射"一词也指创建新的关联的过程。

关联数组所定义的操作有：

* **添加**或**插入**：添加一个新的键值对，建立从新键到新值的映射。该操作的参数是要添加的键和值。
* **重新赋值**：更改一已有的键值对的值，把原有的键映射到新的值。和插入一样，参数为键和值。
* **删除**：移除一个键值对，取消从该键到该值的映射。参数为要删除的键。
* **查询**：查找到给出键对应的值（如果该键存在）。这个操作的参数是要查找的键，返回的是对应的值。如果没有相应的键值对，有些实现会引发异常，而另外一些则会使用所给的键创建并添加新的键值对，其中的"值"为其类型的默认值（零、空容器等）

添加和重新赋值的操作常作为一个**赋值**的操作出现，即如果存在键值对则更新它，反之添加。

关联数组也可能包括其他操作，比如查询已有的映射数目和构造迭代器以遍历所有映射。遍历的顺序通常由关联数组的实现决定。

多重关连数组（英语：）是关联数组的推广，它允许多个值与一个键关联。

示例
--

假设需要在一种数据结构中表示图书馆的借书情况。一本书一次只能借给一位读者，但一位读者同时可以借阅多本书。因此有关哪本书被哪位读者借出的信息可以用关联数组表示。这个数据结构用Python或JSON的记法可以表示如下：

```
{
 "傲慢与偏见": "小红",
 "呼啸山庄": "小红",
 "远大前程": "小李"
}

```

对键"远大前程"的查询操作会返回"小李"。如果小李还了书，就需要一个删除操作。如果小陈外借了一本书，就需要一个插入操作，导致关联数组更新：

```
{
 "傲慢与偏见": "小明",
 "卡拉马佐夫兄弟": "小陈",
 "呼啸山庄": "小红"
}

```

实现
--

对于非常小的关联数组来说，使用关联列表，即以映射为节点的链表实现较为合理。在这种实现下，进行基本操作的时间与映射的总数呈线性关系。但是这样的实现难度低、运行时间中的常数小，因此仍为较好的选择。

在键仅限于较窄范围时，还有另外一种简单的实现技巧。可以将键直接用于数组的寻址：比如键*k*对应的值就储存在数组元素*A*[*k*]，如果*k*还没有映射，那么在*A*[*k*]存储一个特殊的哨兵值（英语：）来表示。这种实现既简单又很快，每个操作只需要常数时间。其不足之处在于需要相当于整个键空间的储存空间，这意味着除非键空间很小，这种方法并不实用。

实现关联数组的主要方法是散列表和搜索树。

### 散列表实现

条目：散列表
这张图比较了使用链表法和线性探测在大型散列表（远大于缓存容量）中查询元素分别所需要的CPU缓存不命中次数。线性探测的表现更好，是因为它有更好的访问局部性，尽管在散列表快填满时其性能急剧下降。
关联数组最常用的通用实现是使用散列表——数组和散列函数的结合。该散列函数把每个键分散到各自的"桶"中。散列表背后的基本原理是：用下标访问数组是一个简单的常数时间的操作。因此对散列表的操作所需的平均开销只包括计算散列值，和在数组中访问对应的"桶"。正因如此，使用散列表的时间复杂度通常为O(1)，大多数情况下优于其他方法。

散列表需要能处理冲突——散列函数把两个不同的键映射到同一个"桶"的情况。解决这个问题的两个最普遍的方法是单独链表法和开放定址法。单独链表法中，数组不是直接储存值本身，而是储存一个指向另一个容器的指针。这个容器常常是一个关联表，储存着对应同个散列值的所有值。而在开放定址法中如果出现散列冲突，散列表会在数组中决定性地找到一个空位，通常就是查看邻近的下一个位置。

当散列表大部分空着时，开放定址法比单独链表法有着更低的缓存不命中率。但是随着散列表被更多的元素填充，开放定址法的性能指数级地下降。此外，单独链表法使用的内存在多数情况下更少，除非值的大小非常小（小于四倍的指针大小）。

### 树实现

条目：搜索树
#### 自平衡二叉搜索树

实现关联数组另一个常用的方法是使用自平衡二叉搜索树，例如AVL树或红黑树。

与使用散列表相比，使用树有其优点和不足。就最坏情况而言，自平衡二叉搜索树远好于散列表。使用自平衡二叉树的最坏情况的时间复杂度用大O表示法表示，为O(log *n*)。相比之下，使用散列表的最坏情况的时间复杂度则为O(*n*)。此外，和所有二叉搜索树一样，自平衡二叉搜索树中的元素始终按顺序排列。因此，对使用树实现的关联数组的遍历是按从小到大的顺序进行的，而遍历散列表则会呈现看似随机的序列。然而，散列表平均情况的时间复杂度（O(1)）比树要好得多，而且若散列函数选择恰当，最坏情况出现的概率很小。

值得注意的是，自平衡二叉搜索树也可以用来实现采用单独链表法的散列表的"桶"。这种实现保留了散列表的常数查询时间，且保证最坏情况也有O(log *n*)的表现。但是这种做法给实现增添了额外的复杂性且可能降低小型散列表的性能，因为在较小的散列表里，在树中插入元素并维护树的平衡所需的时间会长于直接对链表或类似数据结构中进行线性搜索所需的时间。

各语言 / 库中的支持
-----------

关联数组的内置语法上的支持是在1969年由SNOBOL4最早介入的，当时名字叫做"表格"。TMG提供带有字符串键和整数值的表格。MUMPS将多维关联数组作为它的关键数据结构，带有可选的持久性。SETL支持它们作为集合和映射的一种可能实现。

### C++（标准模板库）

STL 提供了 8 个关联数组容器模板：

| 类模板 | 说明 |
| --- | --- |
| std::map<K, V>  std::unordered\_map<K, V> | 从 K 到 V 类型的一对一字典不带 unordered\_ 前缀的为根据 K 排序的红黑树、带前缀的为散列表（即不排序，故名） |
| std::multimap<K, V>  std::unordered\_multimap<K, V> | 从 K 到 V 的一对多字典 |
| std::set<T>  std::unordered\_set<T> | T 的集合 |
| std::multiset<T>  std::unordered\_multiset<T> | T 的多重集 |

### .Net Framework

C++/CLI 中另有 .Net 所提供的托管实现，见下。

| 类 / 接口 | 说明 |
| --- | --- |
| System.Collections.IDictionary  System.Collections.Generic.IDictionary<K, V> | 字典的接口System.Collections 命名空间中为非泛型版本，即其内容类型为 System.Object 类型 |
| System.Collections.HastTable  System.Collections.Generic.Dictionary<K, V> | 散列表实现的字典 |
| System.Collections.Generic.SortedDictionary<K, V> | 二叉搜索树 |
| System.Collections.SortedList  System.Collections.Generic.SortedList<K, V> | 按键排序的数组 |

外部链接
----

|  |  |
| --- | --- |
|  | 维基词典中的词条「」。 |

* NIST's Dictionary of Algorithms and Data Structures: Associative Array （页面存档备份，存于）

|  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
| 

| * 
* 论
* 编
数据结构 |
| --- |
|  |
| 类型 | 
* 集合
* 容器

 |
|  |
| 抽象类型 | 
* 关联数组
	+ 多重关连数组（英语：）
* 列表
* 堆栈
* 队列
	+ 双端队列
* 优先队列
	+ 双端优先队列
* 集合
	+ 多重集
	+ 併查集
* 可持久化数据结构
	+ 线段树

 |
|  |
| 数组 | 
* 字串
* 位数组
* 环形缓冲器
* 动态数组
* 哈希表
* 哈希数组树（英语：）
* 稀疏矩阵

 |
|  |
| 链（英语：） | 
* 关联表（英语：）
* 链表
* 跳跃列表
* 松散链表（英语：）
* 异或链表

 |
|  |
| 树 | 
* 线段树
* 自平衡二叉查找树
* B树
* 二叉树
	+ AA树
	+ AVL树
	+ 红黑树
	+ 平衡树
	+ 伸展树
	+ 二叉查找树
* 堆
	+ 二叉堆
	+ 左偏树
	+ 二项堆
	+ 斐波那契堆
* R树
	+ R\*树
	+ R+树
	+ Hilbert R树（英语：）
* 前缀树
	+ 哈希树

 |
|  |
| 图 | 
* 有向图
* 有向无环图
* 二元决策图
* 无向图
* 确定性非循环有限自动机（英语：）

 |
|  |
| 数据结构术语 |

 |

|  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
| 

| * 
* 论
* 编
数据类型 |
| --- |
|  |
| 无解释的 | 
* 位元
* 字节
* 三进制位
* 三进制字节
* Word

 |
|  |
| 数值 | 
* 整数
	+ 符号性
	+ 有符号数
	+ 无符号数
* 定点数
* 浮点数
	+ 双精度
	+ 扩展精度（英语：）
	+ 半精度
	+ 迷你浮点数（英语：）
	+ 八倍精度（英语：）
	+ 四倍精度（英语：）
	+ 单精度
* 有理数（英语：）
* 复数（英语：）
* 任意精度算术
* 区间（英语：）

 |
|  |
| 文本 | 
* 字符
* 字符串

 |
|  |
| 指针 | 
* 記憶體位址
	+ 物理地址
	+ 虚拟地址
* 參照

 |
|  |
| 组合 | 
* 代数数据类型
	+ 广义（英语：）
* 数组
* 关联数组
* 类
* 串列
* 对象
	+ 元对象
* 可选类型
* 积类型（英语：）
* 记录
* 集合
* 联合体
	+ 标签

 |
|  |
| 其他 | 
* 布尔型
* 底层类别（英语：）
* 容器
* 枚举类型
* 异常
* 一等函数
* 不透明数据类型（英语：）
* 递归数据类型
* 信号标
* 字串流
* 顶类型（英语：）
* 类型类
* 類型系統
* 单位类型（英语：）
* Void
* 不定型別

 |
|  |
| 相关议题 | 
* 抽象資料型別
* 数据结构
* 介面
* 种类（英语：）
* 元类
* 对象类型（英语：）
* 原始型別与複合型別
* 协议
* 子类型
* C++模板
* 型別構造器
* 参数多态

 |

 |