You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Jetpack Compose的SnapshotStateList中使用MutableSet

在Jetpack Compose中结合MutableSet与mutableStateListOf实现唯一元素列表

方案一:手动维护Set与StateList的同步

这种方式简单直接,额外维护一个MutableSet跟踪已存在元素,在添加/移除操作时同步更新mutableStateListOf创建的列表:

// 用于记录已存在的元素,确保唯一性
private val existingResults = mutableSetOf<ScanResult>()
// 供Compose使用的状态列表,会触发重组
val list by lazy { mutableStateListOf<ScanResult>() }

// 添加元素:仅当元素不存在时才加入列表
fun addScanResult(result: ScanResult) {
    // Set的add方法返回true表示元素之前不存在,添加成功
    if (existingResults.add(result)) {
        list.add(result)
    }
}

// 移除元素:同步更新Set和StateList
fun removeScanResult(result: ScanResult) {
    if (existingResults.remove(result)) {
        list.remove(result)
    }
}

关键注意点

  • 必须重写ScanResult的equals和hashCode:Set依赖这两个方法判断元素是否重复。如果ScanResult是数据类,Kotlin会自动基于所有属性生成这两个方法;如果是普通类,需手动实现(比如根据唯一标识字段如id):
class ScanResult(val id: String) {
    override fun equals(other: Any?): Boolean {
        if (this === other) return true
        if (javaClass != other?.javaClass) return false
        other as ScanResult
        return id == other.id
    }

    override fun hashCode(): Int {
        return id.hashCode()
    }
}

方案二:封装自定义的唯一StateList类

如果希望对外接口和普通mutableStateListOf完全一致,同时自动保证唯一性,可以封装一个实现MutableList接口的自定义类,内部同步维护SnapshotStateList和MutableSet:

import androidx.compose.runtime.snapshots.SnapshotStateList
import androidx.compose.runtime.mutableStateListOf

class UniqueStateList<T>(
    private val stateList: SnapshotStateList<T> = mutableStateListOf(),
    private val backingSet: MutableSet<T> = mutableSetOf()
) : MutableList<T> by stateList {

    // 重写add方法,仅添加不存在的元素
    override fun add(element: T): Boolean {
        return if (backingSet.add(element)) {
            stateList.add(element)
        } else {
            false
        }
    }

    // 重写指定位置添加的方法
    override fun add(index: Int, element: T) {
        if (backingSet.add(element)) {
            stateList.add(index, element)
        }
    }

    // 批量添加:仅加入不存在的元素
    override fun addAll(elements: Collection<T>): Boolean {
        val newElements = elements.filterNot { backingSet.contains(it) }
        return if (newElements.isNotEmpty()) {
            backingSet.addAll(newElements)
            stateList.addAll(newElements)
        } else {
            false
        }
    }

    // 指定位置批量添加
    override fun addAll(index: Int, elements: Collection<T>): Boolean {
        val newElements = elements.filterNot { backingSet.contains(it) }
        return if (newElements.isNotEmpty()) {
            backingSet.addAll(newElements)
            stateList.addAll(index, newElements)
        } else {
            false
        }
    }

    // 移除元素时同步更新Set
    override fun remove(element: T): Boolean {
        return if (backingSet.remove(element)) {
            stateList.remove(element)
        } else {
            false
        }
    }

    // 根据索引移除元素时同步更新Set
    override fun removeAt(index: Int): T {
        val removedElement = stateList.removeAt(index)
        backingSet.remove(removedElement)
        return removedElement
    }

    // 清空时同步清空两个集合
    override fun clear() {
        backingSet.clear()
        stateList.clear()
    }

    // 批量移除元素时同步更新
    override fun removeAll(elements: Collection<T>): Boolean {
        val removed = backingSet.removeAll(elements)
        return if (removed) {
            stateList.removeAll(elements)
        } else {
            false
        }
    }

    // 保留指定元素时同步更新
    override fun retainAll(elements: Collection<T>): Boolean {
        val retained = backingSet.retainAll(elements)
        return if (retained) {
            stateList.retainAll(elements)
        } else {
            false
        }
    }
}

使用方式

直接像普通mutableStateListOf一样初始化即可:

val list by lazy { UniqueStateList<ScanResult>() }

后续调用list.add()、list.remove()等方法时,会自动保证元素唯一性,同时Compose能正确感知列表变化触发重组。


内容的提问来源于stack exchange,提问作者Compose Learner

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.06 12:10:50