将List<List<String>>转为MutableList时偶现崩溃问题求助
问题描述
我的应用使用List<List<String>>存储电台信息,每个子列表对应一个电台的名称、标语、流地址等内容。应用内有个「骰子」(荷兰语称dobbelsteen)功能,用于随机播放电台,搜索功能需要展示这个「骰子」项。
搜索逻辑是:检查「骰子」是否不在目标列表categorieIsSelected中,且列表非空,如果满足条件就把它添加到列表头部。但Google Play Console上报了部分崩溃,我无法复现,多数时候功能正常。
崩溃代码片段:
if (dobbelsteen !in categorieIsSelected && categorieIsSelected.isNotEmpty()) { (categorieIsSelected as MutableList<List<String>>).add(0, dobbelsteen) }
错误日志:
Exception java.lang.UnsupportedOperationException: at java.util.AbstractList.add (AbstractList.java:153) at com.familiekoning.radionedplus.RadiostationsAdapter$getFilter$1.publishResults (RadiostationsAdapter.kt:725) at android.widget.Filter$ResultsHandler.handleMessage (Filter.java:284) at android.os.Handler.dispatchMessage (Handler.java:106) at android.os.Looper.loopOnce (Looper.java:226) at android.os.Looper.loop (Looper.java:313) at android.app.ActivityThread.main (ActivityThread.java:8741) at java.lang.reflect.Method.invoke at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run (RuntimeInit.java:571) at com.android.internal.os.ZygoteInit.main (ZygoteInit.java:1067)
完整搜索过滤函数代码:
override fun getFilter(): Filter = object : Filter() { override fun performFiltering(constraint: CharSequence): FilterResults { var filteredList: MutableList<List<String>> = ArrayList(ArrayList()) if (constraint.isEmpty()) { filteredList = categorieWasSelected as MutableList<List<String>> } else { val filterPattern = constraint.toString().lowercase(Locale.ROOT).trim { it <= ' ' } if (exampleListFull != null) { for (item in categorieWasSelected) { if (item[0].lowercase(Locale.ROOT).contains(filterPattern)) { filteredList.add(item) } } } } val results = FilterResults() results.values = filteredList results.count = filteredList.count() return results } override fun publishResults(constraint: CharSequence, results: FilterResults) { if (results.count > 0 && zoeken) { categorieIsSelected = results.values as MutableList<List<String>> //check of dobbelsteen erin zit bij zoekresultaat. Nee, dan toevoegen. if (dobbelsteen !in categorieIsSelected && categorieIsSelected.isNotEmpty()) { //KT.725 geeft error is nu 726 (categorieIsSelected as MutableList<List<String>>).add(0, dobbelsteen) } //check of dobbelsteen als enige over is. if (dobbelsteen in categorieIsSelected && categorieIsSelected.size == 1) { (categorieIsSelected as MutableList<List<String>>).clear() } notifyDataSetChanged() } else { categorieIsSelected = emptyList() notifyDataSetChanged() } } }
问题分析与解决方案
问题根源
崩溃的核心原因是:你强制把一个不可变的List转换成MutableList,然后调用了add或clear方法。
具体来说:
- 在
performFiltering方法中,当constraint.isEmpty()时,你直接将categorieWasSelected强转为MutableList赋值给filteredList。如果categorieWasSelected实际是不可变列表(比如通过listOf()创建、或其他方法返回的不可变视图),强转只是编译期通过,运行时实际对象还是不可变的。 - 在
publishResults中,你把results.values强转为MutableList赋值给categorieIsSelected,随后调用add或clear方法,此时如果原列表是不可变的,就会抛出UnsupportedOperationException。
另外,filteredList = categorieWasSelected as MutableList<List<String>>这种强转非常危险,你无法保证categorieWasSelected一定是可变列表。
修复方案
解决思路是确保操作的列表始终是真正的可变列表,不要依赖不安全的强转。
1. 修改performFiltering方法
override fun performFiltering(constraint: CharSequence): FilterResults { val filteredList = mutableListOf<List<String>>() if (constraint.isEmpty()) { // 将原列表元素复制到新的可变列表,而非直接强转 filteredList.addAll(categorieWasSelected) } else { val filterPattern = constraint.toString().lowercase(Locale.ROOT).trim { it <= ' ' } if (exampleListFull != null) { for (item in categorieWasSelected) { if (item[0].lowercase(Locale.ROOT).contains(filterPattern)) { filteredList.add(item) } } } } val results = FilterResults() results.values = filteredList results.count = filteredList.size return results }
2. 简化publishResults中的强转
现在filteredList已经是真正的MutableList,无需再强转:
override fun publishResults(constraint: CharSequence, results: FilterResults) { if (results.count > 0 && zoeken) { categorieIsSelected = results.values as MutableList<List<String>> if (dobbelsteen !in categorieIsSelected && categorieIsSelected.isNotEmpty()) { categorieIsSelected.add(0, dobbelsteen) } if (dobbelsteen in categorieIsSelected && categorieIsSelected.size == 1) { categorieIsSelected.clear() } notifyDataSetChanged() } else { categorieIsSelected = emptyList() notifyDataSetChanged() } }
额外优化建议
- 避免用
List<List<String>>嵌套列表存储复杂数据,建议定义数据类(比如RadioStation)来存储电台信息,代码可读性和可维护性会更高。 - 不要依赖强制类型转换,Kotlin中尽量使用安全类型转换(
as?),或提前确保列表的可变性。
内容的提问来源于stack exchange,提问作者Patrick Koning
相关产品推荐
相关产品推荐

