既然我们已经有了 Map、List 和 Array 的大小,那么 count() 有什么用呢?

Ely*_*lye 5 collections kotlin

在 Kotlin 集合(列表、数组和映射)中,为了获取大小,我们已经有了size. 有什么用count()

val list = listOf(1, 2, 3)
list.size
list.count()

val map = mapOf(1 to 1, 2 to 2, 3 to 3)
map.size
map.count()

val array = arrayOf(1, 2, 3)
array.size
array.count()
Run Code Online (Sandbox Code Playgroud)

本质上,如果我们在后台检查它们,它们就会返回size

Mar*_*ery 1

size并且count()来自不同的接口。size定义在 上kotlin.collections.CollectionListSet都实现:

public interface Collection<out E> : Iterable<E> {
    // Query Operations
    /**
     * Returns the size of the collection.
     */
    public val size: Int

    ...
Run Code Online (Sandbox Code Playgroud)

while是定义在(继承自)count()的扩展方法:kotlin.collections.IterableCollection

/**
 * Returns the number of elements in this collection.
 */
public fun <T> Iterable<T>.count(): Int {
    if (this is Collection) return size
    var count = 0
    for (element in this) checkCountOverflow(++count)
    return count
}
Run Code Online (Sandbox Code Playgroud)

因此,仅仅继承自Iterables Collection,包括Lists 和Sets,count()就会自动给出一个方法。我们肯定希望Collection继承 from ,Iterable以便我们可以将 aCollection传递给接受任何Iterable. 然后,最后,Map(不是 a Collection)也获得size属性和方法,以便与和count()保持一致。ListSet

从技术上讲,我想这已经回答了为什么集合具有该方法的问题count(),但它还不是一个令人满意的解释,因为它引发了一个后续问题:那么为什么Collections 具有该size属性?为什么标准库开发人员不干脆添加size,而是让我们调用count()s Collection

count()上面引用的的实现向我们展示了问题的答案:size是性能优化。Collection Iterable当对非n个元素调用时,时间复杂度count()O(n),因为它循环遍历可迭代中的所有元素。当调用 a 时Collection,它应该具有O(1)时间复杂度,因为它只是遵循size属性(希望评估的时间复杂度为O(1) !)。

接下来我们可能会问:这种优化可以通过其他方式实现吗?标准库开发人员不能简单地让他们实现的所有成员函数以O(1) 的Collection时间复杂度提供自己的count()成员函数,而不是使用附加属性使 API 变得复杂吗?

没有。因为扩展是静态解析的- 所以如果您没有size, 而是在 、 、 等上提供了O ( 1)实现,那么当您传递or 或时,这些O(1)实现将不会被使用一个以任意值作为参数并调用其方法的函数;相反,调用将静态解析为O(n)扩展方法。count()ListMapSetListMapSetIterablecount()

因此,让sizecount()两者都存在是能够count()在所有s 上可用并且对于s 来说Iterable也是O(1) 的Collection唯一方法。

最后一点,如果您想知道为什么count()是方法并且size是属性,编码约定提供了线索:

函数与属性 在某些情况下,不带参数的函数可以与只读属性互换。尽管语义相似,但在何时更喜欢其中一种方面存在一些风格约定。

当底层算法满足以下条件时,优先选择属性而不是函数:

  • 不抛出

  • 计算成本低(或在第一次运行时缓存)

  • 如果对象状态未更改,则在调用中返回相同的结果

(加粗我的)。由于count()计算(可能)昂贵但size便宜,因此应该将其count()设为方法和size属性 - 这确实是标准库开发人员所做的。