直接给你讲清楚:5 类迭代器的区别、优缺点、适用场景、底层代价,面试常问、写代码必用。
一、迭代器分类总览(从弱到强)
- 输入迭代器 Input Iterator
- 输出迭代器 Output Iterator
- 前向迭代器 Forward Iterator
- 双向迭代器 Bidirectional Iterator
- 随机访问迭代器 Random Access Iterator
越往下功能越强,但实现成本、性能开销也越大。
二、逐类优缺点 + 适用容器
1. 输入迭代器 Input Iterator
只能读,只能向前走一次,不可回头 代表:istream_iterator、单遍扫描算法
优点
- 极简,几乎无开销
- 适合流式数据(文件、网络流、生成器)
缺点
- 只能读一次,不能重复遍历
- 不支持
--it、不支持多遍扫描 - 不能保存迭代器状态
适用
一次性读取、流式处理、find/count 等单遍算法
2. 输出迭代器 Output Iterator
只能写,只能向前走一次 代表:ostream_iterator、back_inserter
优点
- 超轻量
- 适合“边生成边输出”场景
缺点
- 只能写不能读
- 不可回头、不可重复使用
适用
输出到屏幕/文件、插入容器尾部等
3. 前向迭代器 Forward Iterator
可读可写,可多次遍历,但只能 ++ 代表:forward_list
优点
- 结构最简单(单链表就能实现)
- 插入删除高效,内存紧凑
- 可保存迭代器、可重复遍历
缺点
- 不支持 --it,不能后退
- 不能随机访问(
it+5 不行) - 查找慢 O(n)
适用
单向链表、不需要后退的场景
4. 双向迭代器 Bidirectional Iterator
支持 ++ 和 --,可前进可后退 代表:list、map/set(红黑树)
优点
- 既能遍历又能回退,非常通用
- 插入/删除几乎不失效(list/map 特性)
- 适合需要从后往前遍历的场景
缺点
- 不支持随机访问
it + n、it < end、[] 都不行 - 前进/后退都是 O(1),但跳步要 O(n)
适用
链表、树结构、需要双向遍历但不频繁跳步
5. 随机访问迭代器 Random Access Iterator
最强,支持所有指针操作 代表:vector、deque、原生数组
优点
- 支持任意跳跃:
it + n、it - n、[]、< > <= >= - 算法效率最高:
sort/binary_search 必须用它 - 遍历速度最快(接近原生指针)
缺点
- 只有连续内存容器才能实现 链表、树结构根本做不到
- 底层依赖数组,插入中间元素代价大(移动内存)
适用
需要高速随机访问、排序、二分查找等
三、一张表看懂优缺点对比
| 迭代器类型 | 支持操作 | 优点 | 缺点 | 典型容器 |
|---|---|---|---|---|
| 输入迭代器 | 读、++ | 极小开销,适合流式数据 | 只读、单次遍历、不可回头 | istream |
| 输出迭代器 | 写、++ | 极简,适合输出 | 只写、单次遍历 | ostream |
| 前向迭代器 | 读写、++、多遍遍历 | 结构简单、插入高效 | 不可后退、不能随机访问 | forward_list |
| 双向迭代器 | 读写、++、-- | 可前进后退、通用性强 | 不能随机访问、跳步 O(n) | list/map/set |
| 随机访问迭代器 | 全部操作:+ - [] < > 等 | 最快、功能最强、支持高级算法 | 依赖连续内存、中间插入低效 | vector/deque |
四、核心结论(面试必背)
- 功能越强,限制越大,实现成本越高
- 随机访问最强,但必须连续内存
- 双向迭代器通用性最好,但牺牲随机访问
- 为什么 map/set 不支持随机访问? 因为底层是红黑树,不是数组,无法 O(1) 跳转到第 n 个节点。
- 为什么算法要区分迭代器类型?
-
sort 只能用随机访问迭代器 -
reverse 需要双向迭代器 -
find 只要输入迭代器就行
这是 STL 算法高效的核心:按需降级,不浪费性能。
- 日常开发怎么选?
- 要高速随机访问 → vector + 随机访问迭代器
- 频繁插入删除、不跳步 → list/map + 双向迭代器
- 纯单向、极简结构 → forward_list + 前向迭代器
如果你愿意,我可以再给你总结一段面试背诵版精简回答(30 秒讲完迭代器分类优缺点)。