34

我正在尝试在其中找到尾部功能,List<T>但找不到任何功能。我最终这样做了。

fun <T> List<T>.tail() = this.takeLast(this.size -1)

有一个更好的方法吗?

4

4 回答 4

49

Kotlin 没有内置List<T>.tail()函数,因此实现自己的扩展函数是唯一的方法。尽管您的实现非常好,但可以稍微简化一下:

fun <T> List<T>.tail() = drop(1)

或者,您可以定义扩展属性,而不是扩展函数:

val <T> List<T>.tail: List<T>
  get() = drop(1)

val <T> List<T>.head: T
  get() = first()

然后像这样使用它:

val list = listOf("1", "2", "3")
val head = list.head
val tail = list.tail
于 2016-03-04T23:54:08.413 回答
3

您和@Vladimir Mironov 的解决方案起作用,但它们会自动创建原始列表的急切副本(没有第一个元素),对于较大的列表可能需要很长时间。我将使用一个包装List类来定义它,该类将其方法委托给被包装的类,使用索引调整忽略第一个元素:

private class TailList<T> (private val list: List<T>) : List<T> {
    override val size: Int
        get() = list.size -1

    override fun isEmpty(): Boolean = size == 0

    override fun iterator(): Iterator<T> = listIterator()
    override fun listIterator(): ListIterator<T> = list.listIterator(1)
    override fun listIterator(index: Int): ListIterator<T> = list.listIterator(index + 1)
    override fun subList(fromIndex: Int, toIndex: Int): List<T> = list.subList(fromIndex + 1, toIndex + 1)
    override fun lastIndexOf(element: T): Int = list.lastIndexOf(element) - 1
    override operator fun get(index: Int): T = list[index + 1]

    // The following member functions require the copy of a new list
    override fun containsAll(elements: Collection<T>): Boolean = tailList.containsAll(elements)
    override fun contains(element: T): Boolean = tailList.contains(element)
    override fun indexOf(element: T): Int = tailList.indexOf(element)

    private val tailList by lazy { ArrayList(this) }  // makes a proper copy the elements this list represents
}

您可能会注意到评论后的部分中的功能仍然会急切地复制。我这样做只是为了简单起见。为了记忆,我做了一个lazy tailList属性

它们都可以通过手动迭代集合来实现,而不是进行某种委托。如果这是你喜欢的,我相信你能想出来。

这样,head 和 tail 属性就变成了这样:

val <T> List<T>.tail: List<T> 
    get() =
        if(this.isEmpty())
            throw IllegalStateException("Cannot get the tail of an empty List")
        else
            TailList(this)

val <T> List<T>.head: T
    get() = this[0]  // or first()

如果你真的需要它,我可以添加一个更新来制作最后三个成员函数,这样它们就不会急切地复制。

编辑:注意:如果您遵循 Kotlin 迄今为止遵循的约定,您就不会List像这样使 ' 的尾巴变得懒惰,因为它们的所有功能都在List制作急切的副本。相反,特别是如果您正在使用headtail递归迭代列表,我会看看您是否可以Sequence以某种方式尝试这个包装器的想法。Sequence存在的全部意义在于对集合的懒惰工作。

编辑 2:显然 sublist() 创建了一个视图,因此已经很懒惰了。本质上,我只是教你如何创建子列表的实现,除了我将它缩小到只有尾部。

因此,在这种情况下,只需将 sublist() 用于您的 tail 函数。

于 2016-03-05T04:51:47.280 回答
1

如果使用非可变列表,则使用简单:

fun <T> List<T>.tail(): List<T> =
    if (isEmpty()) throw IllegalArgumentException("tail called on empty list")
    else subList(1, count())
于 2019-04-27T09:14:12.830 回答
0

如果您需要 head 和 tail(这是一个基本的序列操作)并且不急切地消耗流,那么您可能需要一个序列。

fun <T> Sequence<T>.headTail() =
    HeadTailSequence(this)
        .let { it.head to it.iterator.asSequence() }

private class HeadTailSequence<T>(parent: Sequence<T>) : Sequence<T> {
    val iterator = parent.iterator()
    val head: T? = iterator.next()

    override fun iterator(): Iterator<T> {
        return object : Iterator<T> {
            override fun hasNext(): Boolean = iterator.hasNext()
            override fun next(): T = if (iterator.hasNext()) throw NoSuchElementException() else iterator.next()
        }
    }
}

Iterable 的扩展可以以类似的方式完成。

于 2021-08-23T10:13:43.207 回答