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

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

本文整理 Java 常见数据结构的核心原理、简化伪代码,以及 Android Framework / Jetpack / 日常开发中常见的使用位置。伪代码用于理解结构和操作流程,不等同于 JDK 或 Android 源码实现。

总览

数据结构 Java 常见实现 核心特点 Android 常见使用场景
数组 int[]Object[] 连续存储、随机访问快 View 缓存、消息数组、像素数据、资源表
动态数组 ArrayList 自动扩容、按索引访问快 列表数据、Adapter 数据源、Fragment 列表
链表 LinkedList 插入删除灵活、随机访问慢 队列、任务链、较少直接用于 UI 数据
StackArrayDeque 后进先出 Activity 返回栈、Fragment 返回栈、递归解析
队列 QueueArrayDeque 先进先出 MessageQueue、任务调度、事件排队
双端队列 DequeArrayDeque 两端插入删除 滑动窗口、任务队列、栈替代品
哈希表 HashMapHashSet 查询快、无序 缓存、对象索引、系统服务注册
有序映射 TreeMapTreeSet 按 key 排序 区间、优先级、按序遍历
链式哈希表 LinkedHashMap 保持插入顺序或访问顺序 LRU 缓存、图片缓存
稀疏数组 SparseArray int key 场景省内存 Android 替代 HashMap<Integer, V>
位集合 BitSet 用 bit 表示布尔集合 标记位、权限状态、压缩布尔数组
PriorityQueue 快速获取最大/最小优先级元素 调度器、定时任务、优先级任务
TreeNode、自定义结构 层级结构 View 树、布局树、导航图
邻接表、邻接矩阵 表达复杂关系 路由导航、依赖关系、任务 DAG
Trie 自定义实现 前缀匹配快 搜索建议、路由匹配、输入法词典
LRU 缓存 LinkedHashMapLruCache 淘汰最近最少使用数据 图片缓存、资源缓存、网络缓存

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 都与数组式索引访问密切相关。
  • RecyclerViewAdapter 数据源经常从数组或列表读取指定位置的数据。

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 位表达。
  • ViewWindowIntent 等大量使用 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 存取数据
  • 支持基本类型、字符串、数组、ParcelableSerializable
  • 常用于组件通信
  • 数据过大可能导致事务失败或性能问题

伪代码

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 的消息机制由 HandlerLooperMessageQueueMessage 组成。线程中的 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 开始。
  • LinearLayoutFrameLayoutConstraintLayout 都是 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 官方结构,只有性能瓶颈明确时再手写数据结构。
  • 移动端尤其关注内存,因此 SparseArrayLruCache、对象复用这类结构和思想很常见。
相关推荐
xieliyu.1 小时前
计算机网络:Fiddler 抓包工具使用教程 + HTTP 协议报文格式详解
java·笔记·计算机网络·测试工具·http·java-ee·fiddler
步行cgn1 小时前
@Value 详解:Spring 属性注入的核心注解
java·python·spring
RisunJan1 小时前
Android 高频面试题与解答
android
2401_850481171 小时前
UVA815
java
霸道流氓气质1 小时前
Spring AI 多租户隔离方案
java·人工智能·spring
MyBili1 小时前
【安卓开发/搞机】快图浏览(QuickPic)技术解析:为何这款3MB应用仍是本地图库的性能天花板?
android·app·安卓·文件管理·相册·看图软件·手机相册
不会就选b1 小时前
Linux之socket编程(七)----序列化与反序列化(1)
java·linux·服务器
秋名RG1 小时前
Java 工程师学习路线
java·学习方法
秋名RG2 小时前
Java 基础语法
java·开发语言