【问题标题】:Sort mixed list using subtype specific behaviour Kotlin使用特定于子类型的行为 Kotlin 对混合列表进行排序
【发布时间】:2020-11-14 16:55:17
【问题描述】:

我有一个项目列表,它们共享一个基类但非常不同,我想先按它们的类对它们进行排序,然后再按特定于该类的比较器。

例如,SubtypeA 应按特定于该类的属性 foo 排序,而 SubTypeB 应按属性排序 bar1 然后 bar2 特定于该类,我需要对包含两者的列表进行排序。

排序的实际顺序并不重要,只是它在运行时内是一致的,因此可以通过首先对这些对象的两个列表进行排序来检查它们是否相等。

(到目前为止,我刚刚使用了一个惰性属性,将对象添加到集合中并检查其索引,从而为相等的对象提供相同的排序键,但是这个缓存正在增长到可笑的大小,这使得事情变得非常缓慢)

如何按类对这些对象进行分组,然后使用特定于类的比较器?

【问题讨论】:

  • 那么当您对 SubTypeA 和 SubTypeB 的类型进行排序时,它们是使用 foo 还是 bar1 和 bar2 或其他方式排序的?
  • @mattfreake 我不确定我是否理解你的问题。您是在问课程应该按什么顺序排列吗?如果这是问题,那真的没关系,我正在考虑使用类的哈希码或其他东西。编辑:需要明确的是,如果解决方案需要以其他方式订购它们,那很好

标签: class sorting kotlin comparator


【解决方案1】:

这里有一些可能满足您的需求。

我们为所有子类型提供单独的比较器,并简单地按类型分组,并使用它自己的比较器对每个组进行排序,然后简单地按类名对组进行排序,并以连续的方式展平每个组中的元素。

这是演示它的代码示例。

  • 添加了实用函数sortedUsing 来执行此排序
  • 添加了一个实用程序类型TypeComparator,只是为了有更方便的方式传递类型安全类+比较器
fun <T : Any> List<T>.sortedUsing(
        vararg typeComparators: TypeComparator<out T>
): List<T> {

    @Suppress("UNCHECKED_CAST")
    fun <R : T> TypeComparator<R>.sort(list: List<T>): List<R> = (list as List<R>).sortedWith(comparator)

    val comparators = typeComparators.associateBy { it.type }
    return groupBy { it::class }
            .mapValues { (klass, list) ->
                val typeComparator = comparators[klass]
                        ?: typeComparators.firstOrNull { klass.isSubclassOf(it.type) }
                        ?: list.firstOrNull()?.tryMakeComparator()
                        ?: throw IllegalArgumentException("Missing comparator for type: $klass")
                typeComparator.sort(list)
            }
            .map { it }
            .sortedBy { it.key.qualifiedName }
            .flatMap { it.value }
}

@Suppress("UNCHECKED_CAST")
private fun <T : Any> T.tryMakeComparator(): TypeComparator<T>? {
    if (this !is Comparable<*>) {
        return null
    }
    return TypeComparator(this::class as KClass<T>, Comparator { o1, o2 ->
        val c1 = o1 as Comparable<Comparable<T>>
        val c2 = o2 as Comparable<T>
        c1.compareTo(c2)
    })
}

data class TypeComparator<T : Any>(
        val type: KClass<T>,
        val comparator: Comparator<T>
)

如果你愿意,你也可以为类型提供比较器,因为上面 sn-p 中的默认设置是按类全名对类型组进行排序。

通过一些更好的方法来累积某种类型的元素,您可以摆脱未经检查的列表强制转换。

使用示例:

open class Base

data class SubtypeA(
        val foo: Int
) : Base()

data class SubtypeB(
        val bar1: String,
        val bar2: String
) : Base()

fun main() {
    val list = listOf(
            SubtypeA(5),
            SubtypeB("foo", "x"),
            SubtypeA(42),
            SubtypeA(2),
            SubtypeB("bar", "y"),
            SubtypeB("bar", "x")
    )
    val sorted = list.sortedUsing(
            TypeComparator(SubtypeA::class, Comparator.comparing { a: SubtypeA -> a.foo }),
            TypeComparator(SubtypeB::class, Comparator.comparing { b: SubtypeB -> b.bar1 }.thenComparing { b: SubtypeB -> b.bar2 })
    )
    sorted.forEach { println(it) }
    // prints:
    //   SubtypeA(foo=2)
    //   SubtypeA(foo=5)
    //   SubtypeA(foo=42)
    //   SubtypeB(bar1=bar, bar2=x)
    //   SubtypeB(bar1=bar, bar2=y)
    //   SubtypeB(bar1=foo, bar2=x)

}

【讨论】:

  • 非常感谢。但是,有两个问题:这是针对多平台项目的,那么这项工作是否可以用::class 替换::class.java 和用KClass&lt;T&gt; 替换Class&lt;T&gt;?其次,尝试访问比较器对象的类的方法而不是将它们作为参数是否明智?
  • 另外,如果您想处理带有可为空元素的列表,您可以添加另一个扩展名,例如 fun &lt;T : Any&gt; List&lt;T?&gt;.sortedNullableUsing(vararg typeComparators: TypeComparator&lt;out T&gt;): List&lt;T?&gt; = (0 until count { it == null }).map { null } + filterNotNull().sortedUsing(*typeComparators)
猜你喜欢
  • 1970-01-01
  • 2021-02-15
  • 1970-01-01
  • 2012-11-07
  • 2014-11-23
  • 1970-01-01
  • 1970-01-01
  • 2011-02-10
  • 1970-01-01
相关资源
最近更新 更多