Java 常见数据结构与 Android 使用场景
本文整理 Java 常见数据结构的核心原理、简化伪代码,以及 Android Framework / Jetpack / 日常开发中常见的使用位置。伪代码用于理解结构和操作流程,不等同于 JDK 或 Android 源码实现。
总览
| 数据结构 | Java 常见实现 | 核心特点 | Android 常见使用场景 |
|---|---|---|---|
| 数组 | int[]、Object[] |
连续存储、随机访问快 | View 缓存、消息数组、像素数据、资源表 |
| 动态数组 | ArrayList |
自动扩容、按索引访问快 | 列表数据、Adapter 数据源、Fragment 列表 |
| 链表 | LinkedList |
插入删除灵活、随机访问慢 | 队列、任务链、较少直接用于 UI 数据 |
| 栈 | Stack、ArrayDeque |
后进先出 | Activity 返回栈、Fragment 返回栈、递归解析 |
| 队列 | Queue、ArrayDeque |
先进先出 | MessageQueue、任务调度、事件排队 |
| 双端队列 | Deque、ArrayDeque |
两端插入删除 | 滑动窗口、任务队列、栈替代品 |
| 哈希表 | HashMap、HashSet |
查询快、无序 | 缓存、对象索引、系统服务注册 |
| 有序映射 | TreeMap、TreeSet |
按 key 排序 | 区间、优先级、按序遍历 |
| 链式哈希表 | LinkedHashMap |
保持插入顺序或访问顺序 | LRU 缓存、图片缓存 |
| 稀疏数组 | SparseArray |
int key 场景省内存 | Android 替代 HashMap<Integer, V> |
| 位集合 | BitSet |
用 bit 表示布尔集合 | 标记位、权限状态、压缩布尔数组 |
| 堆 | PriorityQueue |
快速获取最大/最小优先级元素 | 调度器、定时任务、优先级任务 |
| 树 | TreeNode、自定义结构 |
层级结构 | View 树、布局树、导航图 |
| 图 | 邻接表、邻接矩阵 | 表达复杂关系 | 路由导航、依赖关系、任务 DAG |
| Trie | 自定义实现 | 前缀匹配快 | 搜索建议、路由匹配、输入法词典 |
| LRU 缓存 | LinkedHashMap、LruCache |
淘汰最近最少使用数据 | 图片缓存、资源缓存、网络缓存 |
1. 数组 Array
原理
数组是一段连续内存空间,元素类型相同,创建后长度固定。通过下标访问元素时,可以根据起始地址和下标直接计算目标位置,因此随机访问很快。
特点
- 查询:
O(1) - 按位置更新:
O(1) - 插入 / 删除:平均
O(n),因为可能需要移动元素 - 长度固定,不能自动扩容
伪代码
java
class SimpleArray {
private int[] data;
private int size;
SimpleArray(int capacity) {
data = new int[capacity];
}
int get(int index) {
checkIndex(index);
return data[index];
}
void set(int index, int value) {
checkIndex(index);
data[index] = value;
}
void add(int value) {
if (size == data.length) {
throw new RuntimeException("array is full");
}
data[size] = value;
size++;
}
private void checkIndex(int index) {
if (index < 0 || index >= size) {
throw new IndexOutOfBoundsException();
}
}
}
Android 中的使用
ViewGroup内部会维护子 View 集合,底层常见思想就是数组或动态数组。- 图片像素、音频采样、二进制协议解析中经常使用
byte[]、int[]。 - 资源表、样式属性数组、
TypedArray都与数组式索引访问密切相关。 RecyclerView、Adapter数据源经常从数组或列表读取指定位置的数据。
2. 动态数组 ArrayList
原理
ArrayList 底层是数组。添加元素时,如果容量不足,会创建更大的数组,并把旧数组内容复制过去。它适合频繁按索引访问和尾部追加。
特点
- 随机访问:
O(1) - 尾部追加:均摊
O(1) - 中间插入 / 删除:
O(n) - 扩容会产生数组复制成本
伪代码
java
class SimpleArrayList<E> {
private Object[] elements = new Object[10];
private int size;
void add(E value) {
ensureCapacity(size + 1);
elements[size] = value;
size++;
}
E get(int index) {
checkIndex(index);
return (E) elements[index];
}
void removeAt(int index) {
checkIndex(index);
for (int i = index; i < size - 1; i++) {
elements[i] = elements[i + 1];
}
elements[size - 1] = null;
size--;
}
private void ensureCapacity(int minCapacity) {
if (minCapacity <= elements.length) {
return;
}
Object[] newArray = new Object[elements.length * 2];
copy(elements, newArray);
elements = newArray;
}
}
Android 中的使用
RecyclerView.Adapter常用List<T>或ArrayList<T>作为数据源。- 页面中的列表、搜索结果、消息列表、表单项列表通常使用
ArrayList。 - Fragment、监听器、任务回调等集合也常用动态数组保存。
- Android 源码中大量组件会用数组列表保存观察者、回调和临时对象。
3. 链表 LinkedList
原理
链表由节点组成,每个节点保存数据和指向下一个节点的引用。双向链表还保存前一个节点引用。链表不要求连续内存,因此插入和删除节点时只需要改变引用,但查找指定位置需要从头或尾遍历。
特点
- 头尾插入 / 删除:
O(1) - 按值查找:
O(n) - 按索引访问:
O(n) - 每个节点需要额外引用空间
伪代码
java
class Node<E> {
E value;
Node<E> next;
}
class SimpleLinkedList<E> {
private Node<E> head;
void addFirst(E value) {
Node<E> node = new Node<>();
node.value = value;
node.next = head;
head = node;
}
E removeFirst() {
if (head == null) {
return null;
}
E value = head.value;
head = head.next;
return value;
}
boolean contains(E target) {
Node<E> current = head;
while (current != null) {
if (current.value.equals(target)) {
return true;
}
current = current.next;
}
return false;
}
}
Android 中的使用
- 事件、任务、回调链可以用链表思想组织。
MessageQueue内部的消息按时间顺序链接起来,整体表现接近链式队列。- 日常 Android 开发中,UI 列表数据通常更适合
ArrayList,因为需要频繁按 position 访问。 - OkHttp 的拦截器链、责任链模式虽然不一定用链表类实现,但思想上类似节点逐个传递。
4. 栈 Stack
原理
栈是一种后进先出结构,最后放入的元素最先取出。Java 老类是 Stack,但现代代码更推荐用 ArrayDeque 实现栈。
特点
- 入栈:
O(1) - 出栈:
O(1) - 查看栈顶:
O(1) - 适合保存路径、调用历史、撤销记录
伪代码
java
class SimpleStack<E> {
private ArrayDeque<E> data = new ArrayDeque<>();
void push(E value) {
data.addLast(value);
}
E pop() {
return data.removeLast();
}
E peek() {
return data.peekLast();
}
boolean isEmpty() {
return data.isEmpty();
}
}
Android 中的使用
- Activity 返回栈:新页面入栈,按返回键时栈顶页面出栈。
- Fragment 返回栈:
FragmentManager.addToBackStack()保存 Fragment 事务历史。 - 导航组件
Navigation维护目的地返回栈。 - XML / JSON 解析、表达式解析、递归遍历都可能使用栈。
5. 队列 Queue
原理
队列是先进先出结构,先进入队列的元素先被处理。它非常适合任务排队、事件排队和生产者消费者模型。
特点
- 入队:
O(1) - 出队:
O(1) - 保证处理顺序
- 可扩展为阻塞队列、延迟队列、优先级队列
伪代码
java
class SimpleQueue<E> {
private ArrayDeque<E> data = new ArrayDeque<>();
void offer(E value) {
data.addLast(value);
}
E poll() {
return data.pollFirst();
}
E peek() {
return data.peekFirst();
}
}
Android 中的使用
Looper+MessageQueue是 Android 线程消息机制的核心。Handler.post(Runnable)会把任务放入目标线程的消息队列。- 主线程 UI 更新、点击事件、生命周期调度都会进入消息循环。
- 线程池内部使用工作队列保存等待执行的任务。
6. 双端队列 Deque
原理
双端队列允许在头部和尾部进行插入、删除。它既能当队列使用,也能当栈使用。ArrayDeque 通常比 Stack 更适合作为栈。
特点
- 头尾插入:
O(1) - 头尾删除:
O(1) - 可模拟栈、队列、滑动窗口
- 不支持按索引高效访问
伪代码
java
class SimpleDeque<E> {
private Object[] elements;
private int head;
private int tail;
void addFirst(E value) {
head = previousIndex(head);
elements[head] = value;
}
void addLast(E value) {
elements[tail] = value;
tail = nextIndex(tail);
}
E removeFirst() {
E value = (E) elements[head];
elements[head] = null;
head = nextIndex(head);
return value;
}
}
Android 中的使用
- 页面历史、撤销重做、滑动窗口统计可以用
Deque。 ArrayDeque可作为轻量级栈,用于遍历 View 树或解析嵌套结构。- 后台任务调度中,如果需要从两端取任务,可以使用双端队列思想。
7. 哈希表 HashMap
原理
哈希表通过 key 的 hashCode() 定位数组下标。多个 key 可能落到同一个位置,这叫哈希冲突。Java 8 之后,冲突链较长时可能从链表转为红黑树,提高极端情况下的查找性能。
特点
- 平均查询 / 插入 / 删除:
O(1) - 极端冲突:可能退化,Java 8 后可树化优化
- 不保证遍历顺序
- key 必须正确实现
equals()和hashCode()
伪代码
java
class SimpleHashMap<K, V> {
private Entry<K, V>[] table = new Entry[16];
void put(K key, V value) {
int index = hash(key) % table.length;
Entry<K, V> current = table[index];
while (current != null) {
if (current.key.equals(key)) {
current.value = value;
return;
}
current = current.next;
}
Entry<K, V> entry = new Entry<>(key, value);
entry.next = table[index];
table[index] = entry;
}
V get(K key) {
int index = hash(key) % table.length;
Entry<K, V> current = table[index];
while (current != null) {
if (current.key.equals(key)) {
return current.value;
}
current = current.next;
}
return null;
}
}
Android 中的使用
- 缓存对象:例如按 id 缓存用户、按 url 缓存请求结果。
- 系统服务注册表可以理解为通过名称映射服务获取逻辑。
- ViewModel 存储、Fragment 结果、SavedState 通常有 key-value 数据结构思想。
- Intent extras、Bundle 内部都体现 key-value 映射思想。
8. HashSet
原理
HashSet 底层通常基于 HashMap,只关心 key 是否存在,不关心 value。适合去重和快速判断成员是否存在。
特点
- 添加 / 删除 / 判断存在:平均
O(1) - 元素不重复
- 不保证顺序
- 依赖
equals()和hashCode()
伪代码
java
class SimpleHashSet<E> {
private HashMap<E, Object> map = new HashMap<>();
private static final Object PRESENT = new Object();
void add(E value) {
map.put(value, PRESENT);
}
boolean contains(E value) {
return map.containsKey(value);
}
void remove(E value) {
map.remove(value);
}
}
Android 中的使用
- 记录已选中的 item id。
- 防止重复请求、重复注册监听器。
- 权限集合、标签集合、脏数据集合。
- Diff、同步、过滤逻辑中常用集合判断是否存在。
9. LinkedHashMap
原理
LinkedHashMap 在哈希表基础上额外维护一条双向链表,因此可以保持插入顺序或访问顺序。设置访问顺序后,最近访问的元素会移动到链表尾部,这常用于实现 LRU 缓存。
特点
- 查询接近
O(1) - 遍历顺序稳定
- 比
HashMap多维护链表,内存略高 - 适合缓存和有序映射
伪代码
java
class LruMap<K, V> extends LinkedHashMap<K, V> {
private int maxSize;
LruMap(int maxSize) {
super(16, 0.75f, true);
this.maxSize = maxSize;
}
protected boolean removeEldestEntry(Map.Entry<K, V> eldest) {
return size() > maxSize;
}
}
Android 中的使用
- Android
LruCache的思想就是保存有限容量数据,并淘汰最近最少使用的项。 - 图片加载库经常使用 LRU 思想做内存缓存。
- 最近搜索、最近打开页面、最近使用资源都适合用访问顺序映射。
- 需要稳定顺序的 key-value 配置也可以用
LinkedHashMap。
10. TreeMap / TreeSet
原理
TreeMap 底层是红黑树,key 会按自然顺序或比较器排序。TreeSet 通常基于 TreeMap 实现。它适合需要有序遍历、范围查询、查找前驱后继的场景。
特点
- 查询 / 插入 / 删除:
O(log n) - key 有序
- 支持范围操作
- 需要 key 可比较或提供
Comparator
伪代码
java
class SimpleTreeMap<K, V> {
private TreeNode<K, V> root;
private Comparator<K> comparator;
void put(K key, V value) {
root = insertAndBalance(root, key, value);
}
V get(K key) {
TreeNode<K, V> node = root;
while (node != null) {
int result = comparator.compare(key, node.key);
if (result == 0) {
return node.value;
}
node = result < 0 ? node.left : node.right;
}
return null;
}
}
Android 中的使用
- 按时间、优先级、区间排序的数据可以用
TreeMap。 - 日志、事件、埋点按时间有序组织时可能用到有序结构。
- 动画帧、定时任务、范围索引可以使用树结构思想。
- 日常 Android 开发不如
HashMap常见,但在需要有序 key 时很有价值。
11. SparseArray
原理
SparseArray 是 Android 提供的稀疏数组结构,适合 int 到对象的映射。它避免了 HashMap<Integer, Object> 中 Integer 装箱带来的额外开销。内部通常用两个数组分别保存 key 和 value,并保持 key 有序,查找时使用二分搜索。
特点
- int key 场景更省内存
- 小到中等规模数据很适合
- 查询通常是二分查找:
O(log n) - 插入可能需要移动数组:
O(n)
伪代码
java
class SimpleSparseArray<E> {
private int[] keys;
private Object[] values;
private int size;
void put(int key, E value) {
int index = binarySearch(keys, size, key);
if (index >= 0) {
values[index] = value;
return;
}
int insertIndex = ~index;
moveRight(keys, insertIndex);
moveRight(values, insertIndex);
keys[insertIndex] = key;
values[insertIndex] = value;
size++;
}
E get(int key) {
int index = binarySearch(keys, size, key);
return index >= 0 ? (E) values[index] : null;
}
}
Android 中的使用
SparseArray<View>常用于按 View id 保存 View,减少HashMap<Integer, View>的装箱开销。SparseBooleanArray常用于保存列表 item 的选中状态。SparseIntArray常用于 int 到 int 的映射,例如类型、状态、资源 id 映射。- Android Framework 源码中大量使用
SparseArray系列结构优化移动端内存。
12. BitSet
原理
BitSet 用一个或多个整数的二进制位表示布尔集合。一个 bit 就能表示 true / false,因此比 boolean[] 更节省空间,尤其适合大量标记位。
特点
- 空间利用率高
- 设置 / 清除 / 判断 bit:接近
O(1) - 支持位运算,如与、或、异或
- 适合大量布尔状态
伪代码
java
class SimpleBitSet {
private long[] words;
void set(int index) {
int wordIndex = index / 64;
int bitIndex = index % 64;
words[wordIndex] |= (1L << bitIndex);
}
boolean get(int index) {
int wordIndex = index / 64;
int bitIndex = index % 64;
return (words[wordIndex] & (1L << bitIndex)) != 0;
}
void clear(int index) {
int wordIndex = index / 64;
int bitIndex = index % 64;
words[wordIndex] &= ~(1L << bitIndex);
}
}
Android 中的使用
- 权限、能力开关、状态标记可以用 bit 位表达。
View、Window、Intent等大量使用 flags 保存状态。- RecyclerView 或列表场景中,可以用位图思想保存大量选中状态。
- 图像处理、字体渲染、协议解析中也常见 bit 操作。
13. 堆 Heap / PriorityQueue
原理
堆是一种完全二叉树,通常用数组实现。最小堆保证父节点小于等于子节点,因此堆顶总是最小元素。Java 的 PriorityQueue 默认是最小堆。
特点
- 获取堆顶:
O(1) - 插入:
O(log n) - 删除堆顶:
O(log n) - 适合优先级调度、Top K、合并有序流
伪代码
java
class MinHeap {
private int[] data;
private int size;
void offer(int value) {
data[size] = value;
siftUp(size);
size++;
}
int poll() {
int result = data[0];
data[0] = data[size - 1];
size--;
siftDown(0);
return result;
}
private void siftUp(int index) {
while (index > 0) {
int parent = (index - 1) / 2;
if (data[parent] <= data[index]) {
break;
}
swap(parent, index);
index = parent;
}
}
}
Android 中的使用
- 定时任务和延迟任务可以用最小堆按触发时间排序。
- 下载、上传、日志上报可以按优先级调度。
- 动画、消息、任务调度内部可能按时间或优先级组织。
- 搜索结果 Top K、排行榜、最近 N 条数据可以用堆优化。
14. 树 Tree
原理
树由节点组成,每个节点可以有多个子节点。根节点没有父节点,叶子节点没有子节点。树适合表达层级结构。
特点
- 天然表达父子关系
- 遍历方式包括 DFS 和 BFS
- 二叉搜索树可支持有序查找
- UI、目录、语法结构都常用树表达
伪代码
java
class TreeNode {
String name;
List<TreeNode> children = new ArrayList<>();
void addChild(TreeNode child) {
children.add(child);
}
}
void dfs(TreeNode node) {
if (node == null) {
return;
}
visit(node);
for (TreeNode child : node.children) {
dfs(child);
}
}
Android 中的使用
- View 层级是典型树结构:
DecorView->ViewGroup->View。 - 布局测量、布局、绘制会沿着 View 树递归执行。
- Compose 也维护组合树,状态变化后对相关节点进行重组。
- XML 文档、导航图、菜单结构都可以看作树。
15. 图 Graph
原理
图由顶点和边组成,用于表达多对多关系。图可以是有向图或无向图,也可以带权重。常见存储方式有邻接表和邻接矩阵。
特点
- 表达关系能力强
- 邻接表适合稀疏图
- 邻接矩阵适合稠密图
- 常见算法包括 BFS、DFS、最短路径、拓扑排序
伪代码
java
class Graph {
private Map<String, List<String>> edges = new HashMap<>();
void addEdge(String from, String to) {
edges.computeIfAbsent(from, key -> new ArrayList<>()).add(to);
}
void bfs(String start) {
Queue<String> queue = new ArrayDeque<>();
Set<String> visited = new HashSet<>();
queue.offer(start);
visited.add(start);
while (!queue.isEmpty()) {
String node = queue.poll();
visit(node);
for (String next : edges.getOrDefault(node, List.of())) {
if (visited.add(next)) {
queue.offer(next);
}
}
}
}
}
Android 中的使用
- Jetpack Navigation 的页面目的地和跳转关系可以看作有向图。
- Gradle 模块依赖、任务依赖是图结构,构建时会进行依赖解析和拓扑排序。
- WorkManager 支持任务链和依赖关系,本质上接近任务图。
- 路由框架、业务流程引擎、权限依赖也常用图思想。
16. Trie 前缀树
原理
Trie 用树结构保存字符串,每条路径表示一个前缀。它适合前缀匹配、自动补全、词典查询。
特点
- 查询字符串:
O(k),k是字符串长度 - 前缀搜索快
- 空间开销可能较大
- 可通过压缩 Trie 优化空间
伪代码
java
class TrieNode {
Map<Character, TrieNode> children = new HashMap<>();
boolean isWord;
}
class Trie {
private TrieNode root = new TrieNode();
void insert(String word) {
TrieNode node = root;
for (char c : word.toCharArray()) {
node = node.children.computeIfAbsent(c, key -> new TrieNode());
}
node.isWord = true;
}
boolean startsWith(String prefix) {
TrieNode node = root;
for (char c : prefix.toCharArray()) {
node = node.children.get(c);
if (node == null) {
return false;
}
}
return true;
}
}
Android 中的使用
- 搜索建议、联系人搜索、输入框自动补全可以使用前缀树思想。
- 输入法词库、拼音匹配、关键字过滤也适合 Trie。
- 路由匹配中,如果需要按路径片段快速查找,也可以使用类似前缀树的结构。
- 大量字符串前缀查询时,Trie 比逐个遍历列表更高效。
17. LRU Cache
原理
LRU 是 Least Recently Used 的缩写,表示淘汰最近最少使用的数据。常见实现是 HashMap + 双向链表:HashMap 负责快速定位节点,双向链表负责维护访问顺序。
特点
- 查询:
O(1) - 插入:
O(1) - 淘汰:
O(1) - 非常适合有限内存缓存
伪代码
java
class LruCache<K, V> {
private int capacity;
private Map<K, Node<K, V>> map = new HashMap<>();
private Node<K, V> head;
private Node<K, V> tail;
V get(K key) {
Node<K, V> node = map.get(key);
if (node == null) {
return null;
}
moveToTail(node);
return node.value;
}
void put(K key, V value) {
Node<K, V> node = map.get(key);
if (node != null) {
node.value = value;
moveToTail(node);
return;
}
Node<K, V> newNode = new Node<>(key, value);
map.put(key, newNode);
addToTail(newNode);
if (map.size() > capacity) {
map.remove(head.key);
remove(head);
}
}
}
Android 中的使用
android.util.LruCache是 Android 官方提供的 LRU 缓存工具。- 图片加载库会用内存 LRU 缓存减少重复解码和网络请求。
- 网络响应、头像、缩略图、字体、反射结果都适合缓存。
- 移动端内存有限,LRU 可以在性能和内存之间取得平衡。
18. Bundle / Map 类 Key-Value 结构
原理
Bundle 是 Android 中常见的 key-value 数据容器,用于在组件之间传递少量结构化数据。它和普通 Map 类似,但还涉及序列化、类型约束和跨进程传输。
特点
- 通过字符串 key 存取数据
- 支持基本类型、字符串、数组、
Parcelable、Serializable - 常用于组件通信
- 数据过大可能导致事务失败或性能问题
伪代码
java
class SimpleBundle {
private Map<String, Object> values = new HashMap<>();
void putString(String key, String value) {
values.put(key, value);
}
String getString(String key) {
Object value = values.get(key);
return value instanceof String ? (String) value : null;
}
}
Android 中的使用
Intent.putExtra()使用 key-value 传递页面参数。Fragment.setArguments(Bundle)保存 Fragment 初始化参数。onSaveInstanceState(Bundle)保存临时 UI 状态。- Navigation Safe Args 底层也会把参数放到 Bundle 类结构中传递。
19. MessageQueue 消息链
原理
Android 的消息机制由 Handler、Looper、MessageQueue、Message 组成。线程中的 Looper 不断从 MessageQueue 中取消息,然后分发给对应的 Handler 处理。消息按执行时间排序。
特点
- 一个线程通常对应一个
Looper MessageQueue保存待处理消息Handler负责发送和处理消息- 主线程 UI 更新依赖这套机制
伪代码
java
class SimpleLooper {
private MessageQueue queue;
void loop() {
while (true) {
Message msg = queue.next();
if (msg == null) {
continue;
}
msg.target.dispatchMessage(msg);
}
}
}
class SimpleHandler {
private MessageQueue queue;
void post(Runnable task) {
Message msg = Message.obtain();
msg.callback = task;
msg.target = this;
queue.enqueueMessage(msg);
}
void dispatchMessage(Message msg) {
if (msg.callback != null) {
msg.callback.run();
} else {
handleMessage(msg);
}
}
}
Android 中的使用
- 主线程的事件循环就是
Looper.loop()驱动的。 View.post()、Handler.post()会把任务加入消息队列。- 点击事件、绘制请求、生命周期回调都和主线程消息调度有关。
- ANR 的本质之一是主线程消息长时间无法被处理。
20. View 树
原理
Android UI 是一棵树。ViewGroup 是容器节点,可以包含子 View;普通 View 是叶子节点。测量、布局、绘制、事件分发都会沿着这棵树传递。
特点
- 适合表达嵌套 UI
- 遍历通常是递归或栈 / 队列
- 父节点影响子节点测量和布局
- 事件分发沿树结构传递
伪代码
java
abstract class View {
void measure() {}
void layout() {}
void draw() {}
}
class ViewGroup extends View {
private List<View> children = new ArrayList<>();
void addView(View child) {
children.add(child);
}
@Override
void draw() {
drawSelf();
for (View child : children) {
child.draw();
}
}
}
Android 中的使用
Activity的窗口中最终会挂载一个根 View,一般从DecorView开始。LinearLayout、FrameLayout、ConstraintLayout都是ViewGroup。RecyclerView也是ViewGroup,但它通过复用子 View 优化大量列表项。- 自定义 ViewGroup 时,需要理解树形测量、布局和绘制流程。
常见选择建议
| 场景 | 推荐结构 |
|---|---|
| 需要按索引访问列表项 | ArrayList |
| 需要先进先出任务队列 | ArrayDeque / Queue |
| 需要后进先出历史记录 | ArrayDeque |
| 需要快速 key-value 查询 | HashMap |
| int key 映射对象且关注内存 | SparseArray |
| 需要去重和快速存在判断 | HashSet |
| 需要保留插入顺序 | LinkedHashMap |
| 需要最近最少使用缓存 | LruCache / LinkedHashMap |
| 需要排序 key 或范围查询 | TreeMap |
| 需要按优先级取任务 | PriorityQueue |
| 需要表达层级结构 | 树 |
| 需要表达多对多关系 | 图 |
| 需要前缀搜索 | Trie |
面试回答模板
text
这个数据结构的核心是:
1. 底层存储方式是什么
2. 查询、插入、删除的时间复杂度是什么
3. 空间开销在哪里
4. Android 中哪个组件或场景使用了它
5. 为什么这个场景适合它
学习建议
- 先把数据结构和 Android 原理绑定起来记,例如:
MessageQueue = 队列 / 链式消息结构,ViewGroup = 树,LruCache = HashMap + 双向链表。 - 面试时不要只背复杂度,最好结合 Android 场景说明为什么选它。
- 日常开发中优先使用标准库和 Android 官方结构,只有性能瓶颈明确时再手写数据结构。
- 移动端尤其关注内存,因此
SparseArray、LruCache、对象复用这类结构和思想很常见。