Android Kotlin中Store列表多字段动态排序问题求助
门店列表排序问题解决方案
问题现状
开发Android门店列表应用时,按用户选择的多SortTag排序出现以下问题:
- 整数类型字段仅按字符串首字符排序(如10排在2前面)
- Double类型字段排序逻辑异常
- 多标签排序时,后续标签会覆盖前面的排序结果,未实现优先级链式排序
错误原因分析
- 多标签排序逻辑错误:当前代码遍历
selectedTags时,每次都基于原列表重新排序,而非在前一次排序结果基础上进行二次排序,导致仅最后一个标签生效。 - Comparator类型处理缺陷:
- 数值类型判断后未执行
return,最终执行字符串比较逻辑,整数被当作字符串排序 - Double类型使用
abs(valueFirst-valueSecond)丢失正负信息,无法正确反映升/降序 - Float类型直接返回减法结果(Float转Int会丢失精度,不符合
compare方法返回Int的规范)
- 数值类型判断后未执行
修复后的完整代码
1. 修复多标签排序函数
使用fold链式组合多个Comparator,实现优先级排序(先按第一个标签排,相同则按第二个,以此类推):
fun sortListByAdditionalTags( list: MutableList<Store>, selectedTags: List<SortTag> ): MutableList<Store> { // 初始Comparator:按名称兜底排序 val baseComparator = Comparator<Store> { o1, o2 -> o1.name.compareTo(o2.name) } // 链式组合所有选中的排序规则 val finalComparator = selectedTags.fold(baseComparator) { acc, sortTag -> acc.thenComparing(AdditionalSortComparator(sortTag.key, sortTag.isAscending)) } return list.sortedWith(finalComparator).toMutableList() }
2. 修复自定义Comparator
修正数值类型比较逻辑,确保每个类型分支正确返回Int结果,避免字符串比较兜底:
class AdditionalSortComparator( private val sortProperty: String?, private val isAscending: Boolean ) : Comparator<Store> { // 提前缓存反射属性,避免每次compare都查找 private val sortingDataProperty = Store::class.declaredMemberProperties.first { it.name == "sortingData" } private val targetProperty = sortProperty?.let { SortingData::class.declaredMemberProperties.firstOrNull { prop -> prop.name == it } } override fun compare(o1: Store?, o2: Store?): Int { if (o1 == null || o2 == null) return 0 val s1 = sortingDataProperty.get(o1) as SortingData val s2 = sortingDataProperty.get(o2) as SortingData targetProperty?.let { prop -> val value1 = prop.get(s1) val value2 = prop.get(s2) return when (value1) { is Int -> compareValues(value1, value2) * if (isAscending) 1 else -1 is Float -> value1.compareTo(value2) * if (isAscending) 1 else -1 is Double -> value1.compareTo(value2) * if (isAscending) 1 else -1 else -> compareValues(value1.toString(), value2.toString()) * if (isAscending) 1 else -1 } } // 无匹配属性时按名称排序 return o1.name.compareTo(o2.name) * if (isAscending) 1 else -1 } }
3. 性能优化方案(可选)
为避免反射带来的性能开销,可预定义属性映射替代反射:
// 预定义SortingData属性的获取函数 private val sortingPropertyMap = mapOf( "averageProductPrice" to { s: SortingData -> s.averageProductPrice as Comparable<Any> }, "bestMatch" to { s: SortingData -> s.bestMatch as Comparable<Any> }, "deliveryCosts" to { s: SortingData -> s.deliveryCosts as Comparable<Any> }, "distance" to { s: SortingData -> s.distance as Comparable<Any> }, "minCost" to { s: SortingData -> s.minCost as Comparable<Any> }, "newest" to { s: SortingData -> s.newest as Comparable<Any> }, "popularity" to { s: SortingData -> s.popularity as Comparable<Any> }, "ratingAverage" to { s: SortingData -> s.ratingAverage as Comparable<Any> } ) // 修改Comparator使用预定义映射 class AdditionalSortComparator( private val sortProperty: String?, private val isAscending: Boolean ) : Comparator<Store> { override fun compare(o1: Store?, o2: Store?): Int { if (o1 == null || o2 == null) return 0 val propertyGetter = sortingPropertyMap[sortProperty] ?: return o1.name.compareTo(o2.name) * if (isAscending) 1 else -1 val value1 = propertyGetter(o1.sortingData) val value2 = propertyGetter(o2.sortingData) return value1.compareTo(value2) * if (isAscending) 1 else -1 } }
关键修复点说明
- 链式排序:通过
fold和thenComparing实现多优先级排序,前一个标签排序结果相同时,才会使用下一个标签排序 - 数值类型正确比较:使用
compareValues(针对Int)和compareTo(针对Float/Double)获取正确的Int比较结果,避免字符串兜底 - 方向控制:通过乘以
1或-1统一处理升/降序逻辑,简化代码 - 性能优化:预缓存反射属性或使用预定义映射,减少反射调用的性能损耗
内容的提问来源于stack exchange,提问作者Nayab
相关产品推荐
相关产品推荐

