01-16-17 迭代器模式 - Cursor的数据遍历设计

模式定义

迭代器模式(Iterator Pattern)属于行为型设计模式,其核心思想是:提供一种方法顺序访问一个聚合对象中的各个元素,而又不暴露该对象的内部表示。客户端通过统一的迭代接口遍历不同类型的集合,无需关心集合的底层数据结构。

解决的问题

聚合对象(如数组、链表、树、数据库结果集)的内部结构各不相同,如果客户端直接依赖具体的数据结构进行遍历,那么更换底层实现时所有遍历代码都需要修改。迭代器模式将遍历逻辑从聚合对象中抽离,封装到独立的迭代器对象中,使得客户端代码与集合的内部结构解耦。

类图说明

┌─────────────────┐         ┌──────────────────┐
│   Aggregate      │         │    Iterator       │
│─────────────────│         │──────────────────│
│ + iterator():    │────────>│ + hasNext(): bool │
│   Iterator       │ creates │ + next(): Element │
└────────┬────────┘         └────────┬─────────┘
         │ implements                │ implements
┌────────┴────────┐         ┌────────┴─────────┐
│ConcreteAggregate│         │ ConcreteIterator  │
│─────────────────│         │──────────────────│
│ - elements[]    │<────────│ - aggregate      │
│ + iterator()    │  holds  │ - currentIndex   │
└─────────────────┘  ref    │ + hasNext()      │
                            │ + next()         │
                            └──────────────────┘

模式中的角色:

  • Iterator(迭代器接口):声明遍历所需的 hasNext()next() 等方法
  • ConcreteIterator(具体迭代器):实现遍历逻辑,维护当前遍历位置
  • Aggregate(聚合接口):声明创建迭代器的工厂方法
  • ConcreteAggregate(具体聚合):返回与自身数据结构匹配的具体迭代器

Android源码中的实现

案例一:Cursor 数据库结果集遍历

源码路径

  • frameworks/base/core/java/android/database/Cursor.java(接口)
  • frameworks/base/core/java/android/database/AbstractCursor.java(抽象实现)
  • frameworks/base/core/java/android/database/sqlite/SQLiteCursor.java(SQLite 实现)
  • frameworks/base/core/java/android/database/CursorWrapper.java(装饰器)

Cursor 是 Android 数据库访问的核心接口,其设计完全遵循迭代器模式。Cursor 将数据库查询结果集抽象为可遍历的序列,客户端无需了解数据在底层是如何存储和读取的。

// frameworks/base/core/java/android/database/Cursor.java

public interface Cursor extends Closeable {

    // ---- 迭代器核心方法 ----

    // 获取结果集总行数
    int getCount();

    // 获取当前游标位置(从 0 开始,初始值为 -1)
    int getPosition();

    // 移动到第一条记录(相当于 reset + next)
    boolean moveToFirst();

    // 移动到最后一条记录
    boolean moveToLast();

    // 移动到下一条记录,返回 false 表示已到末尾
    boolean moveToNext();

    // 移动到上一条记录(支持双向遍历)
    boolean moveToPrevious();

    // 移动到指定位置(支持随机访问)
    boolean moveToPosition(int position);

    // 边界检查
    boolean isFirst();
    boolean isLast();
    boolean isBeforeFirst();
    boolean isAfterLast();

    // ---- 数据访问方法 ----

    // 按列索引获取数据
    String getString(int columnIndex);
    int getInt(int columnIndex);
    long getLong(int columnIndex);
    float getFloat(int columnIndex);
    double getDouble(int columnIndex);
    byte[] getBlob(int columnIndex);

    // 按列名获取列索引
    int getColumnIndex(String columnName);
    int getColumnIndexOrThrow(String columnName);
    String getColumnName(int columnIndex);
    String[] getColumnNames();
    int getColumnCount();

    // ---- 资源管理 ----
    void close();
    boolean isClosed();
}
// frameworks/base/core/java/android/database/AbstractCursor.java

public abstract class AbstractCursor implements CrossProcessCursor {

    // 当前游标位置,初始值为 -1(表示在第一条记录之前)
    protected int mPos;

    // 缓存的结果集行数
    protected int mCount = NO_COUNT;

    @Override
    public final boolean moveToFirst() {
        return moveToPosition(0);
    }

    @Override
    public final boolean moveToLast() {
        return moveToPosition(getCount() - 1);
    }

    @Override
    public final boolean moveToNext() {
        return moveToPosition(mPos + 1);
    }

    @Override
    public final boolean moveToPrevious() {
        return moveToPosition(mPos - 1);
    }

    @Override
    public final boolean moveToPosition(int position) {
        // 边界检查:目标位置超出范围
        final int count = getCount();
        if (position >= count) {
            mPos = count;  // 设置为 afterLast
            return false;
        }
        if (position < 0) {
            mPos = -1;  // 设置为 beforeFirst
            return false;
        }

        // 已经在目标位置,无需移动
        if (position == mPos) {
            return true;
        }

        // 调用子类的具体移动实现
        boolean result = onMove(mPos, position);
        if (!result) {
            mPos = -1;
        } else {
            mPos = position;
        }
        return result;
    }

    // 钩子方法:子类实现具体的游标移动逻辑
    // SQLiteCursor 在此方法中加载对应位置的数据到 CursorWindow
    public boolean onMove(int oldPosition, int newPosition) {
        return true;
    }

    @Override
    public final boolean isFirst() {
        return mPos == 0 && getCount() != 0;
    }

    @Override
    public final boolean isLast() {
        int count = getCount();
        return mPos == (count - 1) && count != 0;
    }

    @Override
    public final boolean isBeforeFirst() {
        return getCount() == 0 || mPos == -1;
    }

    @Override
    public final boolean isAfterLast() {
        return getCount() == 0 || mPos == getCount();
    }
}
// frameworks/base/core/java/android/database/sqlite/SQLiteCursor.java

public class SQLiteCursor extends AbstractWindowedCursor {

    private final SQLiteCursorDriver mDriver;
    private final String mEditTable;
    private final SQLiteQuery mQuery;
    private int mCount = NO_COUNT;

    @Override
    public int getCount() {
        if (mCount == NO_COUNT) {
            // 延迟计算:首次访问时才执行 COUNT 查询
            fillWindow(0);
        }
        return mCount;
    }

    // 填充 CursorWindow(共享内存块),加载指定位置附近的数据
    private void fillWindow(int requiredPos) {
        clearOrCreateWindow(getDatabase().getPath());

        try {
            // requiredPos 不在当前窗口范围内时,重新加载
            if (mCount == NO_COUNT) {
                mCount = mQuery.fillWindow(
                    mWindow, requiredPos,
                    requiredPos, true);
            } else {
                mQuery.fillWindow(
                    mWindow, requiredPos,
                    requiredPos, false);
            }
        } catch (RuntimeException ex) {
            // 窗口填充失败时的异常处理
            mCount = 0;
            throw ex;
        }
    }

    @Override
    public boolean onMove(int oldPosition, int newPosition) {
        // 当新位置超出当前 CursorWindow 的范围时,重新加载数据
        if (mWindow == null
            || newPosition < mWindow.getStartPosition()
            || newPosition >= (mWindow.getStartPosition()
                              + mWindow.getNumRows())) {
            fillWindow(newPosition);
        }
        return true;
    }
}

关键设计分析

  • Cursor 接口不仅支持单向遍历(moveToNext),还支持双向遍历(moveToPrevious)和随机访问(moveToPosition),功能远超标准迭代器
  • AbstractCursor 将位置管理和边界检查等通用逻辑封装在基类中,通过模板方法 onMove() 将具体的数据加载委托给子类
  • SQLiteCursor 使用 CursorWindow(共享内存块)实现数据的分页加载——结果集可能有上万条记录,但 CursorWindow 只加载当前窗口内的数据,实现了内存高效的惰性加载
  • CursorWrapper 采用装饰器模式包装 Cursor,可以在不修改原始 Cursor 的情况下添加过滤、变换等功能

案例二:ViewGroup 的子 View 遍历

源码路径frameworks/base/core/java/android/view/ViewGroup.java

ViewGroup 管理着一组子 View,虽然没有显式实现 Iterator 接口,但其子 View 遍历机制体现了迭代器模式的思想。

// frameworks/base/core/java/android/view/ViewGroup.java

public abstract class ViewGroup extends View
        implements ViewParent, ViewManager {

    // 内部数组存储子 View
    private View[] mChildren;

    // 子 View 数量(不等于 mChildren.length,数组可能预分配了更大空间)
    private int mChildrenCount;

    // 获取子 View 数量 —— 对应 Iterator 的边界条件
    public int getChildCount() {
        return mChildrenCount;
    }

    // 按索引获取子 View —— 对应 Iterator 的随机访问
    public View getChildAt(int index) {
        if (index < 0 || index >= mChildrenCount) {
            return null;
        }
        return mChildren[index];
    }

    // 遍历分发绘制 —— 迭代器模式在绘制流程中的应用
    @Override
    protected void dispatchDraw(Canvas canvas) {
        final int childrenCount = mChildrenCount;
        final View[] children = mChildren;

        // 按绘制顺序遍历子 View
        // preorderedList 支持自定义绘制顺序(Z 轴排序)
        ArrayList<View> preorderedList =
            usingRenderNodeProperties ? null : buildOrderedChildList();

        for (int i = 0; i < childrenCount; i++) {
            final int childIndex = getAndVerifyPreorderedIndex(
                childrenCount, i, customOrder);
            final View child = getAndVerifyPreorderedView(
                preorderedList, children, childIndex);

            if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE
                    || child.getAnimation() != null) {
                // 绘制单个子 View
                more |= drawChild(canvas, child, drawingTime);
            }
        }
    }

    // 遍历分发事件 —— 迭代器模式在事件处理中的应用
    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        // ...
        if (!canceled && !intercepted) {
            final int childrenCount = mChildrenCount;

            // 反向遍历(Z 轴最高的子 View 优先接收事件)
            for (int i = childrenCount - 1; i >= 0; i--) {
                final int childIndex =
                    getAndVerifyPreorderedIndex(
                        childrenCount, i, customOrder);
                final View child =
                    getAndVerifyPreorderedView(
                        preorderedList, children, childIndex);

                if (!child.canReceivePointerEvents()
                    || !isTransformedTouchPointInView(
                        x, y, child, null)) {
                    continue;
                }

                if (dispatchTransformedTouchEvent(
                        ev, false, child, idBitsToAssign)) {
                    // 子 View 消费了事件
                    break;
                }
            }
        }
        // ...
    }

    // 查找特定子 View —— 遍历匹配
    public View findViewTraversal(int id) {
        if (id == mID) {
            return this;
        }
        final View[] where = mChildren;
        final int len = mChildrenCount;

        for (int i = 0; i < len; i++) {
            View v = where[i];
            v = v.findViewById(id);
            if (v != null) {
                return v;
            }
        }
        return null;
    }
}

关键设计分析

  • ViewGroup 通过 getChildCount() + getChildAt() 提供了索引式遍历接口,隐藏了内部数组的预分配策略(mChildren.length 可能大于 mChildrenCount
  • 不同场景使用不同的遍历策略:绘制时正向遍历(底层 View 先绘制),事件分发时反向遍历(顶层 View 优先接收触摸事件)
  • buildOrderedChildList() 支持按 Z 轴排序遍历,体现了迭代器模式对遍历顺序的灵活控制
  • findViewTraversal() 在遍历过程中递归进入子 ViewGroup,实现了树结构的深度优先遍历

案例三:SparseArray 的遍历

源码路径frameworks/base/core/java/android/util/SparseArray.java

SparseArray 是 Android 提供的轻量级 Map<Integer, Object> 替代方案,其遍历方式也体现了迭代器思想。

// frameworks/base/core/java/android/util/SparseArray.java

public class SparseArray<E> implements Cloneable {

    // 内部使用两个平行数组存储键值对
    private int[] mKeys;
    private Object[] mValues;
    private int mSize;

    // 获取元素数量
    public int size() {
        if (mGarbage) {
            gc();  // 先清理被标记删除的元素
        }
        return mSize;
    }

    // 按索引获取 key —— 遍历所需
    public int keyAt(int index) {
        if (mGarbage) {
            gc();
        }
        if (index >= mSize) {
            throw new ArrayIndexOutOfBoundsException(index);
        }
        return mKeys[index];
    }

    // 按索引获取 value —— 遍历所需
    @SuppressWarnings("unchecked")
    public E valueAt(int index) {
        if (mGarbage) {
            gc();
        }
        if (index >= mSize) {
            throw new ArrayIndexOutOfBoundsException(index);
        }
        return (E) mValues[index];
    }

    // 删除操作并不立即移除元素,而是标记为 DELETED
    // 延迟到下次 gc() 时批量压缩数组
    public void delete(int key) {
        int i = ContainerHelpers.binarySearch(mKeys, mSize, key);
        if (i >= 0) {
            if (mValues[i] != DELETED) {
                mValues[i] = DELETED;
                mGarbage = true;
            }
        }
    }

    // 延迟压缩:在遍历前统一清理被删除的元素
    private void gc() {
        int n = mSize;
        int o = 0;
        int[] keys = mKeys;
        Object[] values = mValues;

        for (int i = 0; i < n; i++) {
            Object val = values[i];
            if (val != DELETED) {
                if (i != o) {
                    keys[o] = keys[i];
                    values[o] = val;
                    values[i] = null;
                }
                o++;
            }
        }
        mGarbage = false;
        mSize = o;
    }
}

标准遍历方式:

SparseArray<String> sparseArray = new SparseArray<>();
sparseArray.put(10, "ten");
sparseArray.put(20, "twenty");
sparseArray.put(30, "thirty");

// 通过 size() + keyAt() / valueAt() 遍历
for (int i = 0; i < sparseArray.size(); i++) {
    int key = sparseArray.keyAt(i);
    String value = sparseArray.valueAt(i);
    Log.d(TAG, key + " -> " + value);
}

关键设计分析

  • SparseArray 没有实现 Iterable 接口,而是通过 size() + keyAt() / valueAt() 提供索引式遍历,避免创建迭代器对象的开销
  • mGarbage 延迟删除机制:delete() 只做标记,gc() 在遍历前批量压缩。这种设计避免了删除操作导致数组频繁移动
  • 双数组(mKeys + mValues)的存储方式相比 HashMapEntry 对象减少了内存分配,适合 Android 内存敏感的环境
  • 由于 keyAt()valueAt() 在遍历前会触发 gc(),遍历过程中的索引是稳定的

源码设计分析

为什么 Android 选择迭代器模式

Cursor 场景:数据库查询结果集的大小不可预知——可能是几条记录,也可能是数万条。将全部数据一次性加载到内存是不可接受的。Cursor 通过迭代器模式实现了按需加载:CursorWindow 只加载当前窗口的数据,onMove() 在游标移出窗口范围时触发新一轮数据加载。客户端通过 moveToNext() 逐条遍历,完全感知不到背后的分页加载机制。

ViewGroup 场景:View 树是一个递归结构,不同的操作需要不同的遍历顺序(绘制用正序,事件分发用倒序,布局测量用正序)。通过将遍历逻辑内置于各个操作方法中(而非暴露裸数组给外部),ViewGroup 可以灵活控制遍历策略。

SparseArray 场景:Android 移动设备内存有限,标准 HashMap<Integer, Object> 的每个 Entry 都是一个独立对象,自动装箱的 Integer 键也会产生额外内存分配。SparseArray 使用原始 int[] 数组存储键,通过索引式遍历避免创建迭代器对象,极大降低了内存压力和 GC 频率。

优缺点权衡

优点

  • 封装内部结构:客户端通过统一接口遍历,更换底层数据结构不影响遍历代码
  • 支持多种遍历策略:同一聚合可以提供正序、倒序、过滤等不同的迭代器
  • 惰性求值:Cursor 的 CursorWindow 机制实现了数据的按需加载,节省内存
  • 单一职责:遍历逻辑从聚合类中分离,聚合类专注于数据管理

缺点

  • 额外的类层次:引入迭代器接口和实现类,增加了类的数量
  • 遍历性能开销:对于简单数组,直接索引访问比通过迭代器间接访问更快
  • 并发修改风险:遍历过程中如果聚合对象被修改,可能导致不可预期的行为(ConcurrentModificationException

实战应用

场景一:分页数据迭代器

在 IoT 应用中,设备事件日志可能有数千条,需要分页加载。

/**
 * 分页迭代器:自动管理分页加载,对外提供连续的遍历体验。
 * 适用于列表数据量大、需要按需加载的场景。
 */
class PagingIterator<T>(
    private val pageSize: Int = 20,
    private val fetcher: suspend (page: Int, size: Int) -> List<T>
) {
    private var currentPage = 0
    private var currentData = emptyList<T>()
    private var index = 0
    private var exhausted = false

    /**
     * 检查是否还有更多元素。
     * 当前页数据用尽时自动加载下一页。
     */
    suspend fun hasNext(): Boolean {
        if (index < currentData.size) return true
        if (exhausted) return false

        // 加载下一页
        currentData = fetcher(currentPage, pageSize)
        currentPage++
        index = 0

        if (currentData.size < pageSize) {
            exhausted = true  // 最后一页
        }
        return currentData.isNotEmpty()
    }

    /**
     * 获取下一个元素
     */
    suspend fun next(): T {
        if (!hasNext()) throw NoSuchElementException(
            "No more elements"
        )
        return currentData[index++]
    }

    /**
     * 重置迭代器到起始位置
     */
    fun reset() {
        currentPage = 0
        currentData = emptyList()
        index = 0
        exhausted = false
    }
}

/**
 * 将分页迭代器转换为 Flow,与 Kotlin 协程生态无缝集成
 */
fun <T> PagingIterator<T>.asFlow(): Flow<T> = flow {
    reset()
    while (hasNext()) {
        emit(next())
    }
}

// 使用示例
class DeviceLogRepository(private val api: DeviceApi) {

    fun getEventLogs(deviceId: String): Flow<EventLog> {
        val iterator = PagingIterator<EventLog>(pageSize = 50) {
            page, size -> api.fetchEventLogs(deviceId, page, size)
        }
        return iterator.asFlow()
    }
}

// 在 ViewModel 中消费
class DeviceLogViewModel(
    private val repository: DeviceLogRepository
) : ViewModel() {

    private val _logs = MutableStateFlow<List<EventLog>>(emptyList())
    val logs: StateFlow<List<EventLog>> = _logs.asStateFlow()

    fun loadLogs(deviceId: String) {
        viewModelScope.launch {
            repository.getEventLogs(deviceId)
                .chunked(20)  // 每 20 条为一批更新 UI
                .collect { batch ->
                    _logs.update { current -> current + batch }
                }
        }
    }
}

场景二:树结构遍历迭代器

IoT 场景中,设备和房间通常构成树形层级结构。

/**
 * 树节点
 */
data class TreeNode<T>(
    val data: T,
    val children: List<TreeNode<T>> = emptyList()
)

/**
 * 深度优先迭代器(前序遍历)
 */
class DepthFirstIterator<T>(root: TreeNode<T>) : Iterator<T> {

    private val stack = ArrayDeque<TreeNode<T>>()

    init {
        stack.addLast(root)
    }

    override fun hasNext(): Boolean = stack.isNotEmpty()

    override fun next(): T {
        if (!hasNext()) throw NoSuchElementException()
        val node = stack.removeLast()

        // 将子节点逆序压栈,保证左子节点先被访问
        for (i in node.children.indices.reversed()) {
            stack.addLast(node.children[i])
        }

        return node.data
    }
}

/**
 * 广度优先迭代器(层序遍历)
 */
class BreadthFirstIterator<T>(root: TreeNode<T>) : Iterator<T> {

    private val queue = ArrayDeque<TreeNode<T>>()

    init {
        queue.addLast(root)
    }

    override fun hasNext(): Boolean = queue.isNotEmpty()

    override fun next(): T {
        if (!hasNext()) throw NoSuchElementException()
        val node = queue.removeFirst()

        // 将子节点加入队尾
        node.children.forEach { queue.addLast(it) }

        return node.data
    }
}

/**
 * 为 TreeNode 添加可迭代能力
 */
fun <T> TreeNode<T>.depthFirst(): Iterable<T> = Iterable {
    DepthFirstIterator(this)
}

fun <T> TreeNode<T>.breadthFirst(): Iterable<T> = Iterable {
    BreadthFirstIterator(this)
}

// 使用示例:IoT 设备房间树
data class Room(val name: String, val deviceCount: Int)

val home = TreeNode(
    Room("Home", 0),
    listOf(
        TreeNode(
            Room("客厅", 3),
            listOf(
                TreeNode(Room("阳台", 1))
            )
        ),
        TreeNode(
            Room("卧室", 2),
            listOf(
                TreeNode(Room("卫生间", 1)),
                TreeNode(Room("衣帽间", 0))
            )
        )
    )
)

// 深度优先:Home -> 客厅 -> 阳台 -> 卧室 -> 卫生间 -> 衣帽间
home.depthFirst().forEach { room ->
    println("${room.name}: ${room.deviceCount} 台设备")
}

// 广度优先:Home -> 客厅 -> 卧室 -> 阳台 -> 卫生间 -> 衣帽间
home.breadthFirst().forEach { room ->
    println("${room.name}: ${room.deviceCount} 台设备")
}

场景三:Cursor 的 Kotlin 扩展

封装 Cursor 的迭代逻辑,避免在业务代码中反复编写 while (cursor.moveToNext()) 样板代码。

/**
 * 将 Cursor 转换为 Sequence,支持 Kotlin 标准库的
 * map/filter/take 等操作,并自动管理资源释放。
 */
fun <T> Cursor.asSequence(transform: (Cursor) -> T): Sequence<T> {
    return sequence {
        use { cursor ->  // use 会在结束时自动 close
            while (cursor.moveToNext()) {
                yield(transform(cursor))
            }
        }
    }
}

/**
 * 将 Cursor 映射为对象列表
 */
fun <T> Cursor.toList(transform: (Cursor) -> T): List<T> {
    return asSequence(transform).toList()
}

// 使用示例
data class DeviceRecord(
    val id: Long,
    val name: String,
    val type: Int,
    val isOnline: Boolean
)

fun ContentResolver.queryDevices(): List<DeviceRecord> {
    val cursor = query(
        DeviceContract.CONTENT_URI,
        arrayOf("_id", "name", "type", "is_online"),
        null, null, "name ASC"
    ) ?: return emptyList()

    return cursor.toList { c ->
        DeviceRecord(
            id = c.getLong(0),
            name = c.getString(1),
            type = c.getInt(2),
            isOnline = c.getInt(3) == 1
        )
    }
}

// 结合 Sequence 的惰性求值:只取前 10 台在线设备
fun ContentResolver.queryOnlineDevices(limit: Int = 10):
        List<DeviceRecord> {
    val cursor = query(
        DeviceContract.CONTENT_URI,
        arrayOf("_id", "name", "type", "is_online"),
        null, null, null
    ) ?: return emptyList()

    return cursor.asSequence { c ->
        DeviceRecord(
            id = c.getLong(0),
            name = c.getString(1),
            type = c.getInt(2),
            isOnline = c.getInt(3) == 1
        )
    }
    .filter { it.isOnline }
    .take(limit)
    .toList()
}

与其他模式的对比

迭代器模式 vs 访问者模式

维度 迭代器模式 访问者模式
关注点 按顺序访问每个元素 对每个元素执行特定操作
操作定义 遍历逻辑在迭代器中,处理逻辑在客户端 处理逻辑封装在访问者中
元素类型 通常同质(同一类型的元素) 通常异质(不同类型的元素)
典型场景 Cursor 遍历数据库结果 编译器对 AST 节点的操作

迭代器模式 vs 组合模式

组合模式定义了树形结构,迭代器模式定义了遍历策略。二者经常配合使用:ViewGroup(组合模式)管理 View 树的结构,遍历子 View 时使用迭代器模式的思想。findViewTraversal() 对 View 树的深度优先遍历就是二者协作的典型案例。

Cursor vs Java Iterator

维度 Cursor Java Iterator
遍历方向 双向(moveToNext / moveToPrevious) 单向(next)
随机访问 支持(moveToPosition) 不支持
数据访问 提供类型化访问方法(getString/getInt) 返回泛型对象
资源管理 需要显式 close 无资源管理
适用场景 大数据集的分页遍历 内存中集合的遍历

总结与最佳实践

迭代器模式的核心价值在于将遍历逻辑从数据结构中分离,使客户端代码通过统一接口访问不同类型的聚合对象。Android 中的 Cursor 是这一模式在数据库访问领域的经典实现。

最佳实践

  1. 优先使用 Kotlin 标准库的迭代能力SequenceIterableFlow 等已经内建了丰富的遍历和变换操作,自定义迭代器前应评估标准库是否已经满足需求

  2. 注意资源释放Cursor 等持有系统资源的迭代器必须在使用完毕后关闭。Kotlin 的 use {} 扩展函数可以保证异常情况下资源也能正确释放

  3. 惰性求值优先:对于大数据集,使用 Sequence(惰性)而非 List(急切)可以避免将全量数据加载到内存。CursorCursorWindow 机制正是惰性求值的系统级实现

  4. 防止遍历期间修改:遍历过程中修改聚合对象会导致不可预期的行为。如果需要在遍历时删除元素,应使用 Iterator.remove() 或收集待删除元素后统一处理

  5. 索引式遍历的性能优势:在 Android 性能敏感的场景(如 onDraw、事件分发),for (i in 0 until count) + getChildAt(i) 的索引式遍历比创建 Iterator 对象更高效,避免了对象分配开销

  6. 选择合适的遍历策略:树结构选择深度优先还是广度优先取决于业务场景。搜索特定元素时深度优先更省内存,按层级处理时广度优先更直观

Logo

欢迎加入DeepSeek 技术社区。在这里,你可以找到志同道合的朋友,共同探索AI技术的奥秘。

更多推荐