我有一个狗类和一个猫类。我想创建一个列表,其中有 2 只狗,然后是 1 只猫,一遍又一遍,直到我们用完狗。该列表应按狗的顺序对狗进行排序。在 Kotlin 中执行此操作最有效的方法是什么?
这是我当前正在运行的代码:
enum class CatBreed { MAINE_COON, BALINESE, BENGAL}
enum class DogBreed { ITALIAN_GREYHOUND, BEAGLE, AKITA, POMERANIAN, SHIH_TZU, DACHSHUND }
data class Dog(val breed: DogBreed, val order: Int)
data class Cat(val breed: CatBreed)
abstract class PetsItem
data class DogItem(val dog: Dog): PetsItem()
data class CatItem(val cat: Cat): PetsItem()
var petsList = arrayListOf<PetsItem>()
val dogs = arrayListOf(Dog(ITALIAN_GREYHOUND, 3), Dog(BEAGLE, 2), Dog(AKITA, 1), Dog(POMERANIAN, 6), Dog(SHIH_TZU, 5), Dog(DACHSHUND, 4))
val cats = arrayListOf(Cat(MAINE_COON), Cat(BALINESE), Cat(BENGAL))
val orderedDogs = ArrayList(dogs.asSequence()
.sortedBy { it.order }
.toList())
for ((index, dog) in orderedDogs.withIndex()) {
petsList.add(DogItem(dog = dog))
if (index % 2 == 1) {
if (cats.isNotEmpty()) {
petsList.add(CatItem(cat = cats.removeAt(0)))
}
}
}
我有这个代码:
val list = ArrayList(dogs.asSequence()
.sortBy { it.order }
.map { DogItem(dog = it) }
.toList())
这似乎非常有效地将 Dog 对象转换为 DogItem 对象。但我不知道如何修改它以允许猫。
那么有谁知道是否有比我目前正在做的更有效的方法来创建我想要的列表?
我认为对于这么小的列表,这已经足够有效了:
val petsList: List<PetsItem> = dogs
.sortedBy(Dog::order)
.map(::DogItem)
.zip2To1(cats.map(::CatItem))
尽管仅使用内置函数就可以将狗列表与猫列表压缩在一起,但尊重所有边缘情况将非常困难,而且可能效率低下。这就是我使用定制函数的原因:
fun <T> Iterable<T>.zip2To1(other: Iterable<T>): List<T> {
val iteratorThis = iterator()
val iteratorOther = other.iterator()
if (!iteratorThis.hasNext()) return emptyList()
if (!iteratorOther.hasNext()) return if (this is List) this else this.toList()
val result = mutableListOf<T>()
result.add(iteratorThis.next())
while (iteratorThis.hasNext()) {
result.add(iteratorThis.next())
result.add(iteratorOther.next())
if (!iteratorOther.hasNext()) iteratorThis.forEachRemaining { result.add(it) }
if (iteratorThis.hasNext()) result.add(iteratorThis.next())
}
return result
}
这比 for 循环更有效,因为它不需要从可变的 cats 列表中删除元素,它只是将两个列表一起迭代。它还具有的好处是您的列表可以是不可变的,并且可以轻松地与其他列表转换链接在一起。
此解决方案仅使用列表操作,与您在示例中使用的序列相反。当您有许多元素和长转换链时,序列非常有用,因为元素不会复制到新列表并在每次转换时再次迭代,这一切都在一次迭代中完成。不过,设置它们确实会产生额外的成本,因为它们是基于协程的,并且需要在序列产生新值(然后挂起)时来回切换。在没有实际对它进行基准测试的情况下,我仍然认为对于这种特定情况,列表会更适合。
也就是说,您可以轻松地将所有内容切换为序列:
val dogs = sequenceOf(...)
val cats = sequenceOf(...)
就是这样。
petsList
不需要改动,因为标准库为序列提供(大部分)与列表相同的转换函数。如果您希望最终将结果序列转换为列表,您只需附加一个 .toList()
。您现在唯一缺少的是序列的自定义 zip2To1
函数。嗯,这里是:
fun <T> Sequence<T>.zip2To1(other: Sequence<T>): Sequence<T> {
val iteratorThis = iterator()
val iteratorOther = other.iterator()
if (!iteratorThis.hasNext()) return emptySequence()
if (!iteratorOther.hasNext()) return this
return sequence {
yield(iteratorThis.next())
while (iteratorThis.hasNext()) {
yield(iteratorThis.next())
yield(iteratorOther.next())
if (!iteratorOther.hasNext()) yieldAll(iteratorThis)
if (iteratorThis.hasNext()) yield(iteratorThis.next())
}
}
}