Home > database >  Any vs Nested Concrete type in Kotlin Collections
Any vs Nested Concrete type in Kotlin Collections

Time:12-05

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 Strings, 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.

  • Related