Java 常见数据结构与 Android 使用场景

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、对象复用这类结构和思想很常见。
相关推荐
const_qiu5 分钟前
第四章 05:4.5 Multi-Agent —— 五个专职智能体协作
java·人工智能
A小码哥12 分钟前
大模型成本怎么降,5 个动作先做前两个
java·前端·spring
鬼手点金16 分钟前
构建有效 Agent 的核心原则
java·人工智能·git·学习·自动化
神魔皆以血嗜21 分钟前
Java 实现简单状态机(订单状态流转)
java·后端
老牛日常随笔22 分钟前
Google把Android测试搬进AI对话:ARTEMIS如何用自然语言操作你的手机
android·人工智能·智能手机
随性而行36030 分钟前
企业微信 API 如何实现业务消息自动通知?接口应用场景解析
java·人工智能·后端·微信·机器人·企业微信
姜九笙56136 分钟前
python学习---归来仍是新人
java·python·学习
Code_Solitude44 分钟前
传智杯备赛代码整理(前4题)
数据结构·算法
HEJOO91 小时前
Java 并发运行中的安全问题详解
java·开发语言
2601_962106641 小时前
【无标题】
java·idea