Kotlin 集合中的任何与嵌套具体类型
Any vs Nested Concrete type in Kotlin Collections
是否使用 Any 作为集合类型比使用具体类型消耗更少的内存?
假设
val list1 = listOf<Any>("ABC", "DEF", "GHI", "JKL", "MNO")
val list2 = listOf<String>("ABC", "DEF", "GHI", "JKL", "MNO")
我想知道 list1
是否比 list2
消耗更少的内存,因为 String
类型分配内存来存储其属性(例如 size
)
那么,如果我不使用任何 String
类型的函数,那么使用 list1
会更好吗?
编辑
如果我想在集合中使用其他类型怎么办?
list = listOf<Any>("ABC", 123, 12.34)
比
效率高吗
list = listOf<String>("ABC", "123", "12.34")
编辑 2
感谢@João Dias 和@gidds
如@gidds 所说:
the list does not directly contain String objects, or Any objects — it contains references.
And a String reference is exactly the same size as an Any reference or a reference of any other type.
因此,List<String>
和 List<Any>
完全相同,因为 Type Erasure -- 指出@João Dias——编译时和运行时类型不同
但是,这是否意味着
val list1 = listOf<Any>("ABC", "DEF", "GHI")
和
val list2 = listOf<String>("ABC", "DEF", "GHI")
消耗的内存与
相同
val list3 = listOf<List<List<List<String>>>>(
listOf(listOf(ListOf("ABC"))),
listOf(listOf(ListOf("DEF"))),
listOf(listOf(ListOf("GHI")))
)
AFAIK,String
基本上是 Char
的集合。 String
包含对 Char
的引用。由于在 Kotlin 中一切都是对象,因此 String
中的每个 Char
都应该包含对堆中值的引用,我到这里是正确的吗 ?
如果是这样的话,List<String>
比 List<Any>
消耗更多的内存没有意义,因为 List<String>
有超过 1 个引用。
它对内存消耗没有任何影响。您正在构建内容完全相同的完全相同的列表。此外,还有一种叫做类型擦除的东西,它是这样的:
Type erasure can be explained as the process of enforcing type
constraints at compile-time and discarding the element type
information at runtime.
这意味着在运行时,没有 List<String>
或 List<Any>
只是 List
在内存消耗方面使用第一个或第二个没有任何区别。在代码可读性、可维护性和健壮性方面,你绝对应该选择 List<String>
。鉴于在 Kotlin 中公开为 List
的列表在默认情况下是只读的(感谢@Alex.T 和@Tenfour04 的提示,列表只能被认为是不可变的如果它们中的元素也是不可变的,如果 List
的具体实现确实是不可变的,例如,考虑一个 list : List<String>
属性 和一个底层可变的 ArrayList<String>
那么 list
仍然不是完全不可变的,因为 since cast 允许您从中添加或删除元素 (list as ArrayList<String>).add("new-element")
) 您基本上只有使用 List<Any>
的缺点(因为如果您想迭代或使用它的任何那时你所知道的是 Any
元素比 String
).
这样的特定类型更难处理。
目前尚未解决的一点是 列表不直接包含 String
个对象,或 Any
个对象——它包含 references.
并且 String
引用与 Any
引用或任何其他类型的引用的大小完全相同。 (该大小取决于 JVM 的内部 运行 代码;它可能是 4 或 8 个字节。参见 these questions。)
当然,被引用的对象也会在堆中占用自己的space;但这两种情况都是一样的。
编辑添加:
如何实现 List
和 String
的内部细节与原始问题无关。 (这很好,因为它们在实现之间有所不同。) JVM 语言(例如 Kotlin)只有两种值:原语(Int
、Short
、Long
、Byte
、Char
、Double
、Float
、Boolean
)和引用(对象或数组)。
所以任何集合,如果不是基元集合,就是引用集合。这适用于所有 List
实施。因此,您的 list1
和 list2
对象的大小将完全相同,仅取决于它们持有(或可以持有)的引用数量,而不是 in这些引用。
如果您想要更深入的了解,list1
是一个引用,指向一个实现了 List
接口的对象。有许多不同的实现,我不知道 Kotlin 会选择哪一个(同样,这可能会在不同版本之间发生变化),但是举个例子,它是 ArrayList
。它至少有两个属性:一个大小(可能是一个 Int
),以及一个对数组的引用,该数组包含对列表中项目的引用。 (数组通常会比列表的当前大小大,这样你就可以添加更多的项目而不必每次都重新分配数组;数组的当前大小被称为列表的 capacity.)‖如果这些项是 String
,那么确切的内部表示取决于 JVM 版本,但它可能是一个至少具有三个属性的对象:Char
的数组,一个 Int
给出数组中字符串的起始索引,另一个 Int
给出长度。
但正如我所说,细节会随着时间和 JVM 版本的不同而变化。不变的是 List
是引用的集合,引用的大小不依赖于它的类型。因此,String
引用列表(所有其他条件相同)将采用与 Any
引用相同字符串的列表完全相同的 space。
(而且,正如其他地方提到的,由于运行时的类型擦除,JVM 没有类型参数的概念,因此对象实际上是相同的。)
当然,“深度大小”(列表及其包含的对象占用的整个堆space)将取决于那些的大小对象 — 但在我们讨论的情况下,这些是完全相同的 String
对象,因此它们的大小也没有差异。
是否使用 Any 作为集合类型比使用具体类型消耗更少的内存?
假设
val list1 = listOf<Any>("ABC", "DEF", "GHI", "JKL", "MNO")
val list2 = listOf<String>("ABC", "DEF", "GHI", "JKL", "MNO")
我想知道 list1
是否比 list2
消耗更少的内存,因为 String
类型分配内存来存储其属性(例如 size
)
那么,如果我不使用任何 String
类型的函数,那么使用 list1
会更好吗?
编辑
如果我想在集合中使用其他类型怎么办?
list = listOf<Any>("ABC", 123, 12.34)
比
效率高吗list = listOf<String>("ABC", "123", "12.34")
编辑 2
感谢@João Dias 和@gidds
如@gidds 所说:
the list does not directly contain String objects, or Any objects — it contains references.
And a String reference is exactly the same size as an Any reference or a reference of any other type.
因此,List<String>
和 List<Any>
完全相同,因为 Type Erasure -- 指出@João Dias——编译时和运行时类型不同
但是,这是否意味着
val list1 = listOf<Any>("ABC", "DEF", "GHI")
和
val list2 = listOf<String>("ABC", "DEF", "GHI")
消耗的内存与
相同val list3 = listOf<List<List<List<String>>>>(
listOf(listOf(ListOf("ABC"))),
listOf(listOf(ListOf("DEF"))),
listOf(listOf(ListOf("GHI")))
)
AFAIK,String
基本上是 Char
的集合。 String
包含对 Char
的引用。由于在 Kotlin 中一切都是对象,因此 String
中的每个 Char
都应该包含对堆中值的引用,我到这里是正确的吗 ?
如果是这样的话,List<String>
比 List<Any>
消耗更多的内存没有意义,因为 List<String>
有超过 1 个引用。
它对内存消耗没有任何影响。您正在构建内容完全相同的完全相同的列表。此外,还有一种叫做类型擦除的东西,它是这样的:
Type erasure can be explained as the process of enforcing type constraints at compile-time and discarding the element type information at runtime.
这意味着在运行时,没有 List<String>
或 List<Any>
只是 List
在内存消耗方面使用第一个或第二个没有任何区别。在代码可读性、可维护性和健壮性方面,你绝对应该选择 List<String>
。鉴于在 Kotlin 中公开为 List
的列表在默认情况下是只读的(感谢@Alex.T 和@Tenfour04 的提示,列表只能被认为是不可变的如果它们中的元素也是不可变的,如果 List
的具体实现确实是不可变的,例如,考虑一个 list : List<String>
属性 和一个底层可变的 ArrayList<String>
那么 list
仍然不是完全不可变的,因为 since cast 允许您从中添加或删除元素 (list as ArrayList<String>).add("new-element")
) 您基本上只有使用 List<Any>
的缺点(因为如果您想迭代或使用它的任何那时你所知道的是 Any
元素比 String
).
目前尚未解决的一点是 列表不直接包含 String
个对象,或 Any
个对象——它包含 references.
并且 String
引用与 Any
引用或任何其他类型的引用的大小完全相同。 (该大小取决于 JVM 的内部 运行 代码;它可能是 4 或 8 个字节。参见 these questions。)
当然,被引用的对象也会在堆中占用自己的space;但这两种情况都是一样的。
编辑添加:
如何实现 List
和 String
的内部细节与原始问题无关。 (这很好,因为它们在实现之间有所不同。) JVM 语言(例如 Kotlin)只有两种值:原语(Int
、Short
、Long
、Byte
、Char
、Double
、Float
、Boolean
)和引用(对象或数组)。
所以任何集合,如果不是基元集合,就是引用集合。这适用于所有 List
实施。因此,您的 list1
和 list2
对象的大小将完全相同,仅取决于它们持有(或可以持有)的引用数量,而不是 in这些引用。
如果您想要更深入的了解,list1
是一个引用,指向一个实现了 List
接口的对象。有许多不同的实现,我不知道 Kotlin 会选择哪一个(同样,这可能会在不同版本之间发生变化),但是举个例子,它是 ArrayList
。它至少有两个属性:一个大小(可能是一个 Int
),以及一个对数组的引用,该数组包含对列表中项目的引用。 (数组通常会比列表的当前大小大,这样你就可以添加更多的项目而不必每次都重新分配数组;数组的当前大小被称为列表的 capacity.)‖如果这些项是 String
,那么确切的内部表示取决于 JVM 版本,但它可能是一个至少具有三个属性的对象:Char
的数组,一个 Int
给出数组中字符串的起始索引,另一个 Int
给出长度。
但正如我所说,细节会随着时间和 JVM 版本的不同而变化。不变的是 List
是引用的集合,引用的大小不依赖于它的类型。因此,String
引用列表(所有其他条件相同)将采用与 Any
引用相同字符串的列表完全相同的 space。
(而且,正如其他地方提到的,由于运行时的类型擦除,JVM 没有类型参数的概念,因此对象实际上是相同的。)
当然,“深度大小”(列表及其包含的对象占用的整个堆space)将取决于那些的大小对象 — 但在我们讨论的情况下,这些是完全相同的 String
对象,因此它们的大小也没有差异。