使用 Kotlin 按字母顺序对数组进行排序

18

我正在为我的手机制作一个启动器,需要按字母顺序对应用程序进行排序。

 Appslist = ArrayList<AppInfo>()

    val i = Intent(Intent.ACTION_MAIN, null)
    i.addCategory(Intent.CATEGORY_LAUNCHER)
    val allApps = this.packageManager.queryIntentActivities(i, 0)

    for (ri in allApps) {
        val app = AppInfo()
        app.label = ri.loadLabel(this.packageManager)
        app.packageName = ri.activityInfo.packageName
        app.icon = ri.activityInfo.loadIcon(this.packageManager)
        if(app.label?.toString()!!.length >= searchWord.length && app.label?.toString()!!.substring(0, searchWord.length) == searchWord.toUpperCase() && searchWord != "" ||
            app.label?.toString()!!.length >= searchWord.length && app.label?.toString()!!.substring(0, searchWord.length) == searchWord.toLowerCase() && searchWord != "" ||
            app.label?.toString()!!.length >= searchWord.length && app.label?.toString()!!.substring(0, searchWord.length) == searchWord.capitalize() && searchWord != "" ||
            app.label?.toString()!!.length >= searchWord.length && app.label?.toString()!!.substring(0, searchWord.length) == searchWord && searchWord != ""){

            if(app.packageName != "com.david.launcher" ){
                Appslist.add(app)
            }

        }
        if(searchWord == ""){
            if(app.packageName != "com.david.launcher"){
                Appslist.add(app)
            }
        }

    }

这是我的列表类型(我不太确定它是否被称为列表类型,但我希望你能理解):


public class AppInfo {
internal var label: CharSequence? = null
internal var packageName: CharSequence? = null
internal var icon: Drawable? = null
internal var isInFav: Boolean? = false

使用 Collections.sort(your_list) 这个方法,可以给你一个已排序的列表。 - pratik vekariya
@pratikvekariya 您的评论是不正确的,这是一个类列表,它们没有实现 Comparable 接口,因此没有自然顺序,您的方法会失败,并且它也不符合 Kotlin 的惯用法。 - Jayson Minard
David,你的问题已经解答了吗?还是你还在寻找其他的东西? - Jayson Minard
是的,它是。 - David Wenzel
3个回答

45

如果你想要对列表进行排序并生成副本,通常的做法是使用 sortedBy 扩展方法来操作 List。或者,如果你想要进行原地排序而不需要生成副本,则可以在 MutableList 上使用 sortBy 扩展方法。无论使用哪种列表类型,ArrayList 都能胜任。

// Sort a readonly list into a copy of the list

val appsList: List<AppInfo> = ...

val sortedAppsList = appsList.sortedBy { it.label?.toString() }

对比:

// Sort a mutable list in-place

val appsList: MutableList<AppInfo> = ...

appList.sortBy { it.label?.toString() }

如果作为ArrayList持有,它是相同的,但直接引用这个具体类型不是惯用法。

// Sort an ArrayList list into a copy of the list

val appsList: ArrayList<AppInfo> = ...  // ALERT! not idiomatic

val sortedAppsList = appsList.sortedBy { it.label?.toString() }

// or if you want, feel free to sort in-place

appsList.sortBy { it.label?.toString() }

注意 label: CharSequence 成员上的 toString()。在排序引用类型为 CharSequence 的对象时,必须小心,因为它的排序行为是未定义的(请参见:https://docs.oracle.com/javase/7/docs/api/java/lang/CharSequence.html)。

该接口不细化 equals 和 hashCode 方法的通用契约。因此,比较实现 CharSequence 接口的两个对象的结果在一般情况下是未定义的。

如果 CharSequence 已经是 String(很可能是),那么调用 toString() 没有任何问题,因为它只会返回本身。

还要记住处理可空的 CharSequence,并决定您想让 null 在哪里:在列表开头或结尾。我认为默认是将它们放到开头。


关于您提供的代码的其他说明:

使用 ListMutableList 接口而不是具体类来引用类型,并使用 Kotlin 标准库中的方法执行对列表的操作。对于不会改变引用的引用,请使用 val(这意味着它始终会指向相同的列表,而不管列表内容是否可能发生更改)。

您编写的大型 if 语句可以大大简化,从...

if(app.label?.toString()!!.length >= searchWord.length && app.label?.toString()!!.substring(0, searchWord.length) == searchWord.toUpperCase() && searchWord != "" ||
    app.label?.toString()!!.length >= searchWord.length && app.label?.toString()!!.substring(0, searchWord.length) == searchWord.toLowerCase() && searchWord != "" ||
    app.label?.toString()!!.length >= searchWord.length && app.label?.toString()!!.substring(0, searchWord.length) == searchWord.capitalize() && searchWord != "" ||
    app.label?.toString()!!.length >= searchWord.length && app.label?.toString()!!.substring(0, searchWord.length) == searchWord && searchWord != ""){

    if(app.packageName != "com.david.launcher" ){
        Appslist.add(app)
    }

}
if(searchWord == ""){
    if(app.packageName != "com.david.launcher"){
        Appslist.add(app)
    }
}

简化为:
if (app.packageName != "com.david.launcher" &&
        (searchWord.isBlank() || 
         app.label?.startsWith(searchWord, ignoreCase = true) == true)) {
    appsList.add(app)
}

你应该 浏览标准库,以便了解可用的内容,从而拓展未来的工具箱。


1
class CustomClass {
    var id: String = ""
    var name: String = ""
}

fun sortAlphabetically(arrayList: ArrayList< CustomClass >): ArrayList< CustomClass >{
        var returnList: ArrayList< CustomClass > = arrayListOf()
        var list = arrayList as MutableList< CustomClass >
        list.sortWith(Comparator { o1: CustomClass, o2: CustomClass ->
            o1.name.compareTo(o2.name)
        })
        returnList = list as ArrayList< CustomClass >
        return returnList
    }

0

您可以使用Collections.sort()方法,通过自定义实现一个基于AppInfo.label的Comparator<AppInfo>比较器或者让AppInfo类实现Comparable接口并实现compareTo方法来仅比较标签字段。


这个答案不完整,因为它是一个更复杂的问题。首先,可用于排序的数据类型不可排序(CharSequence),而且在 Kotlin 中创建 Comparator 只用于一次性排序并不符合惯用法,也不应该再为一次性排序添加 Comparable 到类中。此外,由于类中所有类型都是可空的,因此必须考虑空值处理。 - Jayson Minard

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接