Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Array/List iteration without extra object allocations

Tags:

kotlin

I'm working on a game written in Kotlin and was looking into improving GC churn. One of the major sources of churn are for-loops called in the main game/rendering loops that result in the allocation of iterators.

Turning to the documentation, I found this paragraph:

A for loop over an array is compiled to an index-based loop that does not create an iterator object.

If you want to iterate through an array or a list with an index, you can do it this way:

for (i in array.indices)   print(array[i]) 

Note that this “iteration through a range” is compiled down to optimal implementation with no extra objects created.

https://kotlinlang.org/docs/reference/control-flow.html#for-loops

Is this really true? To verify, I took this simple Kotlin program and inspected the generated byte code:

fun main(args: Array<String>) {     val arr = arrayOf(1, 2, 3)     for (i in arr.indices) {         println(arr[i])     } } 

According to the quote above, this should not result in any objects allocated, but get compiled down to a good old pre-Java-5 style for-loop. However, what I got was this:

      41: aload_1       42: checkcast     #23                 // class "[Ljava/lang/Object;"       45: invokestatic  #31                 // Method kotlin/collections/ArraysKt.getIndices:([Ljava/lang/Object;)Lkotlin/ranges/IntRange;       48: dup       49: invokevirtual #37                 // Method kotlin/ranges/IntRange.getFirst:()I       52: istore_2       53: invokevirtual #40                 // Method kotlin/ranges/IntRange.getLast:()I       56: istore_3       57: iload_2       58: iload_3       59: if_icmpgt     93 

This looks to me as if a method called getIndices is called that allocates a temporary IntRange object to back up bounds checking in this loop. How is this an "optimal implementation" with "no extra objects created", or am I missing something?

UPDATE: So, after toying around a bit more and looking at the answers, the following appears to be true for Kotlin 1.0.2:

Arrays:

  • for (i in array.indices): range allocation
  • for (i in 0..array.size): no allocation
  • for (el in array): no allocation
  • array.forEach: no allocation

Collections:

  • for (i in coll.indices) range allocation
  • for (i in 0..coll.size): no allocation
  • for (el in coll): iterator allocation
  • coll.forEach: iterator allocation
like image 318
Matthias Avatar asked Jun 03 '16 08:06

Matthias


People also ask

Why are ArrayLists better than arrays?

The capacity of an Array is fixed. Whereas ArrayList can increase and decrease size dynamically. An Array is a collection of similar items. Whereas ArrayList can hold item of different types.

What is the difference between Array& ArrayList?

The array is a specified-length data structure whereas ArrayList is a variable-length Collection class.

DO FOR EACH loops work with Arraylists?

The enhanced for loop (sometimes called a "for each" loop) can be used with any class that implements the Iterable interface, such as ArrayList .


2 Answers

As far as I know the only allocation-less way to define a for loop is

for (i in 0..count - 1) 

All other forms lead to either a Range allocation or an Iterator allocation. Unfortunately, you cannot even define an effective reverse for loop.

like image 28
Michael Avatar answered Sep 27 '22 21:09

Michael


To iterate an array without allocating extra objects you can use one of the following ways.

  1. for-loop
    for (e in arr) {         println(e)     } 
  1. forEach extension
    arr.forEach {         println(it)     } 
  1. forEachIndexed extension, if you need to know index of each element
    arr.forEachIndexed { index, e ->         println("$e at $index")     } 
like image 150
Ilya Avatar answered Sep 27 '22 20:09

Ilya