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

Kotlin中如何让contains方法忽略指定属性?

问题概述

在Android RecyclerView实现的对象追踪功能中,点击收藏图标后产品的isFavourite属性会改为true。RecyclerView的onScrollStateChanged触发时会调用trackVisibleItems方法,通过shownTrackablePositions.contains(it)判断对象是否已被追踪。但isFavourite属性更新后,contains会将其判定为不同对象,导致重复追踪。尝试将isFavourite放在Kotlin data类主构造函数外仍未解决,需要让contains忽略该属性的方案。

解决方案

方案1:重写equals()和hashCode()方法

Kotlin data类默认会基于主构造函数属性生成equals和hashCode,如果你的业务类继承自TrackableObject,可以手动重写这两个方法,明确排除isFavourite属性的比较:

data class Product(
    val productId: String,
    val productName: String
    // 核心业务属性(不包含isFavourite)
) : TrackableObject() {
    var isFavourite: Boolean = false // 收藏状态放在主构造外

    override fun equals(other: Any?): Boolean {
        if (this === other) return true
        if (javaClass != other?.javaClass) return false
        other as Product
        // 仅比较核心唯一标识属性
        if (productId != other.productId) return false
        if (productName != other.productName) return false
        // 不参与isFavourite的比较
        return true
    }

    override fun hashCode(): Int {
        var result = productId.hashCode()
        result = 31 * result + productName.hashCode()
        // 不加入isFavourite的哈希值
        return result
    }

    // 实现TrackableObject的抽象方法
    override fun getClickEventName(): String = "product_click"
    override fun getViewEventName(): String = "product_view"
    override fun getTrackParams(): MutableSet<Pair<String, Any>> {
        return mutableSetOf(
            "product_id" to productId,
            "is_favourite" to isFavourite
        )
    }
}

修改后,shownTrackablePositions.contains(it)会基于核心属性判断,完全忽略isFavourite的状态变化。

方案2:用唯一标识代替对象存入集合

放弃存储TrackableObject实例,改为存储对象的唯一标识(比如产品ID),从根源上避免属性变化带来的判断错误:

步骤1:调整集合类型

// 将原有的MutableSet<TrackableObject>改为存储唯一ID
private val shownTrackablePositions = mutableSetOf<String>()

步骤2:新增抽象方法并修改追踪逻辑

给TrackableObject新增获取唯一ID的抽象方法:

abstract class TrackableObject {
    abstract fun getUniqueId(): String // 返回对象唯一标识
    // 原有抽象方法...
}

// 产品类实现该方法
data class Product(val productId: String, ...) : TrackableObject() {
    override fun getUniqueId(): String = productId
    // ...
}

修改trackVisibleItems方法:

private fun trackVisibleItems(visiblePositions: IntRange) {
    for (position in visiblePositions) {
        if (position != -1) {
            trackableCollectionCallback?.getViewedItem(position)?.let { item ->
                val itemUniqueId = item.getUniqueId()
                if(isPresent() && !shownTrackablePositions.contains(itemUniqueId)){
                    item.trackView()
                    trackableCollectionCallback?.onTrackedItem(item, position)
                    shownTrackablePositions.add(itemUniqueId)
                }
            }
        }
    }
}

这种方式不受对象属性变化影响,是稳定性最高的方案之一。

方案3:基于对象引用判断(适合实例不重建的场景)

如果RecyclerView不会重新创建TrackableObject实例(仅更新属性而非生成新对象),可以使用基于内存引用的集合,忽略equals方法的影响:

import java.util.Collections

// 创建基于对象引用判断的Set
private val shownTrackablePositions = Collections.newSetFromMap(IdentityHashMap<TrackableObject, Boolean>())

该集合判断对象是否存在时,仅比较内存引用,不管属性如何变化,只要是同一个实例就会被判定为已存在。但如果Adapter刷新时重新生成对象实例,此方案会失效,需根据业务场景选择。


内容的提问来源于stack exchange,提问作者Taimoor Khan

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.09 14:10:25