count() 有什么用,因为我们已经有了 Map、List 和 Array 的大小?



在 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()

从本质上讲,如果我们在引擎盖下检查它们,它们只会返回size.

sizecount()来自不同的接口。size定义在kotlin.collections.Collection上,ListSet都实现了:

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

count()是在kotlin.collections.Iterable上定义的扩展方法(Collection继承自(:

/**
* 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
}

因此,仅从Iterable继承就会自动Collection(包括Lists和Sets(提供count()方法。我们绝对希望CollectionIterable继承,以便我们可以将Collection传递给将接受任何Iterable的方法。然后,最后,Map(不是Collection(为了与ListSet保持一致,也同时获得了size属性和count()方法。

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

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

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

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

因此,sizecount()都存在是能够在所有Iterable上提供count()的唯一方法,同时也使其在Collection秒内为O(1(。

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

函数与属性 在某些情况下,没有参数的函数可能与只读属性互换。尽管语义相似,但对于何时首选一个而不是另一个有一些风格约定。

当基础算法:

  • 不抛

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

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

(粗体我的(。由于count()计算成本(可能(昂贵,但size便宜,因此count()应该成为一种方法并size属性 - 这确实是标准库开发人员所做的。

相关内容

最新更新