Kotlin中如何按列表特定元素过滤?优化空值处理代码
Kotlin 中服务器数据过滤与转换的地道写法
从服务器拉取的电影数据列表可能为空或null,下面是已经实现的基础空值过滤与转换逻辑:
fun filterMovieItemList(unfilteredList: List<MovieItem>): List<MovieItemModel> { val movieItemModelList = mutableListOf<MovieItemModel>() if (unfilteredList.isNotEmpty()) { unfilteredList.forEachIndexed { _, movieItem -> val movieDetails = movieItem.movieDetails if (movieDetails != null && !movieDetails.name.isNullOrEmpty()) { val icon = movieDetails.iconUrl ?: "xyz" movieItemModelList.add( MovieItemModel( movieDetails.name, icon, movieDetails.summary ?: "", ) ) } } } return movieItemModelList }
我尝试用map系列方法优化代码提升可读性,写出了下面的版本,但不确定写法是否正确:
fun filterMapMovieItemList(unfilteredList: List<MovieItem>): List<MovieItemModel> { return unfilteredList.mapNotNull { data -> data.movieDetails?.name?.let { data.movieDetails } }.map { movieItem -> val icon = movieItem.iconUrl ?: "xyz" MovieItemModel( name = movieItem.name ?: "", icon = icon, summary = movieItem.summary ?: "" ) } }
先看你写的优化写法是否正确
这个写法是可行的,但存在可以优化的点:当前代码会遍历两次集合(一次mapNotNull筛选有效数据,一次map转换模型),可以合并为单次遍历提升效率;另外map里的name ?: ""属于冗余判断,因为mapNotNull已经确保name非空。
更地道的Kotlin写法
1. 合并过滤与转换,单次遍历完成
用mapNotNull同时完成条件判断和模型转换,避免多次遍历:
fun filterMovieItemList(unfilteredList: List<MovieItem>): List<MovieItemModel> { return unfilteredList.mapNotNull { movieItem -> val details = movieItem.movieDetails // 筛选出name非空的有效details val validName = details?.name.takeIf { !it.isNullOrEmpty() } ?: return@mapNotNull null val icon = details.iconUrl ?: "xyz" MovieItemModel( name = validName, icon = icon, summary = details.summary ?: "" ) } }
takeIf会在条件满足时返回自身,否则返回null;mapNotNull会自动过滤掉所有null结果,一次遍历完成所有操作。
2. 拆分过滤与转换,逻辑更清晰
如果倾向于把筛选和转换步骤分开,可以用filter+map组合:
fun filterMovieItemList(unfilteredList: List<MovieItem>): List<MovieItemModel> { return unfilteredList.filter { item -> val details = item.movieDetails details != null && !details.name.isNullOrEmpty() }.map { item -> val details = item.movieDetails!! // 已经通过filter校验,可安全解包 val icon = details.iconUrl ?: "xyz" MovieItemModel( name = details.name, icon = icon, summary = details.summary ?: "" ) } }
其他适用的方法
除了map系列方法,还可以结合这些Kotlin特性简化代码:
- 作用域函数(run/let):通过作用域函数减少变量重复引用,让代码更连贯:
fun filterMovieItemList(unfilteredList: List<MovieItem>): List<MovieItemModel> { return unfilteredList.mapNotNull { item -> item.movieDetails?.run { name.takeIf { !it.isNullOrEmpty() }?.let { validName -> MovieItemModel(validName, iconUrl ?: "xyz", summary ?: "") } } } }
- Sequence序列:如果处理的是超大数据量,用
asSequence()转为序列,延迟执行所有操作,减少中间集合的内存开销:
fun filterMovieItemList(unfilteredList: List<MovieItem>): List<MovieItemModel> { return unfilteredList.asSequence() .mapNotNull { it.movieDetails } // 过滤null的details .filter { !it.name.isNullOrEmpty() } // 过滤name为空的项 .map { details -> MovieItemModel(details.name, details.iconUrl ?: "xyz", details.summary ?: "") } .toList() // 最终转为List }
序列的操作是懒加载的,只有调用toList()时才会执行所有步骤,适合大数据量场景。
内容的提问来源于stack exchange,提问作者Compose Learner
相关产品推荐
相关产品推荐

