is using Any as collection type consume less memory than using concrete type?
suppose
val list1 = listOf<Any>("ABC", "DEF", "GHI", "JKL", "MNO")
val list2 = listOf<String>("ABC", "DEF", "GHI", "JKL", "MNO")
i wonder if list1
consume less memory than list2
since String
type allocates memory to store its properties (e.g size
)
So, is it better to use list1
if i dont use any String
type function?
EDIT
what if i want to use other type in the collection?
list = listOf<Any>("ABC", 123, 12.34)
is it more efficient than
list = listOf<String>("ABC", "123", "12.34")
EDIT 2
thanks to @João Dias and @gidds
as @gidds says :
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.
So, The List<String>
and List<Any>
are the exactly the same because of the Type Erasure --which pointed out by @João Dias-- with a difference of compile-time & runtime type
but, does it mean that
val list1 = listOf<Any>("ABC", "DEF", "GHI")
and
val list2 = listOf<String>("ABC", "DEF", "GHI")
is consuming the same memory as
val list3 = listOf<List<List<List<String>>>>(
listOf(listOf(ListOf("ABC"))),
listOf(listOf(ListOf("DEF"))),
listOf(listOf(ListOf("GHI")))
)
AFAIK, String
is basically collections of Char
. A String
contains a reference to the Char
. And since everything is an object in Kotlin, then every Char
inside the String
should contain a reference to the value in the heap, am i correct up to here?
if thats the case, doesnt it make sense that List<String>
consume more memory than List<Any>
because List<String>
is having more than 1 reference.
CodePudding user response:
It does not make any difference in memory consumption. You are building the exact same list with exactly the same content. Additionally, there is a thing called type erasure that goes something like this:
Type erasure can be explained as the process of enforcing type constraints at compile-time and discarding the element type information at runtime.
This means that at runtime, there is no List<String>
or List<Any>
just List
making no difference whatsoever if you use the first or the second in regards to memory consumption. In regards to code readability, maintainability and robustness you definitely should go with List<String>
. Given that in Kotlin lists exposed as List
are read-only by default (thanks @Alex.T and @Tenfour04 for the hints, Lists can only be considered immutable if the elements in them are also immutable and if the concrete implementation of List
is indeed immutable, e.g., consider a list : List<String>
property with an underlying mutable ArrayList<String>
then list
is still not completely immutable since a since cast allows you to add or remove elements from it (list as ArrayList<String>).add("new-element")
) you basically have only disadvantages on using List<Any>
(because then if you want to iterate or use any of its elements all you will know at that time is that it is Any
element which is way harder to work with than a specific type like String
).
CodePudding user response:
A point not addressed so far is that 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. (That size depends on the internals of the JVM running the code; it might be 4 or 8 bytes. See these questions.)
Of course, the objects being referred to will also take up their own space in the heap; but that will be the same in both cases.
EDITED TO ADD:
The internal details of how List
and String
are implemented is irrelevant to the original question. (Which is good, coz they vary between implementations.) JVM languages (such as Kotlin) have only two kinds of value: primitives (Int
, Short
, Long
, Byte
, Char
, Double
, Float
, Boolean
), and references (to an object or an array).
So any collection, if it's not a collection of primitives, is a collection of references. That applies to all List
implementations. So your list1
and list2
objects will be exactly the same size, depending only on the number of references they hold (or can hold), not on what's in those references.
If you want a deeper picture, list1
is a reference, pointing to an object which implements the List
interface. There are many different implementations, and I don't know off-hand which one Kotlin will pick (and again, that might change between versions), but say for example it's an ArrayList
. That has at least two properties: a size (which is probably an Int
), and a reference to an array which holds the references to the items in the list. (The array will usually be bigger than the current size of the list, so that you can add some more items without having to re-allocate the array each time; the current size of the array is known as the list's capacity.) If those items are String
s, then the exact internal representation depends on the JVM version, but it might be an object with at least three properties: an array of Char
, an Int
giving the start index of the string within the array, and another Int
giving either the length.
But as I said, the details change over time and between JVM versions. What doesn't change is that List
is a collection of references, and the size of a reference doesn't depend on its type. So a list of String
references will (all other things being equal) take exactly the same space as a list of Any
references to those same strings.
(And, as has been mentioned elsewhere, due to type erasure at runtime the JVM has no concept of type parameters, and so the objects will in fact be identical.)
Of course, the ‘deep size’ (the overall heap space taken up by the list and the objects it contains) will depend upon the size of those objects — but in the case we're discussing, those are the exact same String
objects, so there's no difference in size there either.