Array initialization

We are used to see Kotlin as a better language, with many features that Java lacks. And in many ways it is a better language. Sometimes though, I find it very confusing why some decisions led to repeat the same issues that Java has. Namely, arrays are one of those features.

Let’s examine array initialization in Kotlin. If you have 1-dimensional array of ints, then:

val size = 5
val array1d = Array<Int>(size) { 0 }

alternatively we have a function that returns an array

val array1d = arrayOf<Int>(1, 2, 3)

That’s pretty simple for 1 dimension but when we have 2-dimensional array then it is something like:

val array = Array<Array<Int>>(5) {
    Array<Int>(5) { 0 }
}

or one of the variations:

val array2d = arrayOf<Array<Int>>()

Now, most of the time, when you are working with an array, it has a “geometric shape”. It can be a square or a rectangle. So why can’t we simply initialize it like this:

val array2d = Array<Int>(n, m) {0} // where n, m are any integers including n = m

Or, even better, C/C++ like array initialization:

val array2dInts = Array<T>[n][m] //with optional initialization for obvious types

So if you have Numeric types or Strings it will initialize it with 0 and empty string respectively. If my array has a type that requires a call to a constructor with some parameters then it can be this:

val array2dChessBoard = Array<ChessPiece>[8][8] { ChessPiece(name = "Bishop") }

It would be much nicer to have a vararg for size and dimensions, either in double square brackets [n][m] or just parentheses (n, m).

P. S. In case we have a staggered array, with rows of different size then sure, we will simply fall back to current initialization constructs. But most of the time it’s not the case.

Kotlin idioms normally prefer usage of List or other more powerful data structures over arrays directly. You notice for example that kotlin does not have a special syntax for array types like java does.
If you need something like a 2d rectangular array you should maybe look for a library that provides this or just create your own class for it. It will make accessing data within that class easier since it will also give you easy control over iteration, equality, etc.

1 Like

It is nice to see a request not about array literals for once. I believe there are some pretty nice math libraries out there that provide matrix and multi-depth array allocation–maybe someone can add a link?

It is true that after the introduction CS courses, you learn an actual array is a very rare thing. I can’t remember the last time I worked on a repo that had a single use of Array other than fun main(args: Array<String>).

I imagine it may be more common for some domains, and I bet coders coming from languages that rally heavily on raw collections (e.x. Python) may to try to replicate that style when coding in Kotlin.

2 Likes

I understand that many times an Iterable Collection is a much better choice that any Array but what if I have a fixed size set of data? I know that it won’t grow or shrink and I’m fine with this data structure.

I think either we get rid of arrays completely and let everyone use Collection types or it’s better to simplify their usage. If this is a good idea or not, we would need to see some data in regards (or some kind of research).

I have used them a lot in production software lately. It makes a lot of sense for some use cases (math, graphs, etc). Creating a library is one way to go but I don’t think it’s the best way. This would look like a language patch more that a library.

Arrays are one of the core data structures. They have been around for many decades and they should be dead simple to use. They are not.

I’m sure it’s not a new idea but more on libraries vs language features:
I see libraries as a collection of useful but complex code that you can reuse across projects. Libraries can change frequently to adapt to new sets of requirements in a more specialized environment.

A language feature should be widely used, small construct that rarely changes.

It’s like @Nullable String s in java vs val s?: String in Kotlin. In java it is part of a library (or better, many libraries have it… which one to choose?) but in Kotlin, the language designers decided that it should be part of the language itself. And because of that, there is a simple and unique way to do that. Clean and tidy.

Are arrays like @Nullable, or primatives?

I like the example with @Nullable. I think Kotlin did sacrifice when they chose nullable types instead of full union types (which I think was a nice pragmatic choice). I don’t think it’s analogous to Arrays though but I can see where you’re coming from.

Here’s an analogy that might help explain where I see it differently: Instead of comparing looking at @Nullable, look at how Kotlin handles primitives–they don’t exist in Kotlin. Kotlin (and other pure OOP languages before it) takes something that a beginner Java coder considers absolutely necessary and throws it out the window for something to create a more sound conceptual model.

Other solutions that need to be considered?

I’m not against improving array usage, I just don’t want to rush into it.

Side note about array literals

Most people I’ve talked to that want literal arrays with a very specific solution and haven’t considered the larger possibilities. Sometimes they haven’t even considered the options for general collection literals (set, map, or arbitrary types).

I like that your OP details the issue because creating multiple dimension collections is the problem. If the issue is instantiating collections, we should solve that use case instead of trying to force-fit the Java syntax. It may be the best option but we should arrive there ourselves.

You can!

fun main() {
    val array2d = array2d<Int>(3, 3) { 0 }
    println(array2d.map { it.toList() }.joinToString("\n"))
}

inline fun <reified T> array2d(n: Int, m: Int, intitializer: () -> T) = Array<Array<T>>(n) {
    Array<T>(m) { intitializer() }
}

Let’s consider a few possibilities for initializing an array (these are just function signature ideas. I didn’t implement all of them):

val myArray = Array<Int>(n, m) {0} // Would require 'namespace' or Companion extension on Array*
val myArray = array<Int>(3,3,3) // 3x3x3 array. Any dimension using vararg but losing safety.

// Or maybe:
val myArray = array3d<Int>[3][3][3] // Odd but possible.

// Or something like this:
val myArray: Array3d<Int> = array(3, 3, 3) // Same as first but keeping type safety.

// Of course we can always typealias for long generic class declarations.
typealias Array3d<T> = Array<Array<Array<T>>>

There’s many options other than pushing through the old system of raw array literals (Java’s array literals) that could help us deal with arrays. Just look at KMath.

Another element that should be part of the discussion is immutable collections. IMO they are often the first thing left out of a request for array literals and are important to consider how changes would enable/limit the options for immutable collections.

How often are array issues a problem?

I agree that for math, graphs, and many more cases, arrays are useful. And under the hood, arrays are essential.

My holdback here is not that it would be nice to create multi-dimension arrays with brackets, but that we also should consider other collections than an array. For example, creating multi-dimension lists I suspect is far more common.
Any syntax we consider would lock us out of other uses as well. One of the popular multi-receiver syntax ideas used brackets, for example.

Of course the minus 100 point rule still applies.

Are arrays commonly the best option for X?

I think it’s worth narrowing the concept of arrays to say that, at a high level for almost all uses, “array” is used to mean “fixed-size list” (even to the point that I would earmark an API using an array as a potential code smell in case they could be changed to a List).
I really like how Kotlin has removed a lot of the magic behind Arrays and made them feel more like a normal object.

What about in the future?

Personally, I do think Kotlin will add collection literals at some point down the line. I see this going three ways:

  1. Array literals and declaration syntax (Java style) - This is the weakest option IMO–it’s also the most requested option that I’ve seen. It makes a rarely used collection special while passing up on better opportunities.
  2. A few collection literal types (Groovy / Python style) - This option is allowing syntax for a few different types of collection literals and/or type declarations.
  3. General collection syntax or something else - There’s plenty of other ideas to explore. One is allowing users to support literal collection syntax for their own class. I don’t have much to say about this. I’ll leave that to more creative with language design.

Requests for collection literals have been discussed here and elsewhere at great length, and it’s probably not a good idea to start all that again…

2 Likes

Note that an Array is always a one dimensional structure so it doesn’t make that sense to introduce two-dimensional syntax.
Instead, create a new type Matrix<T> which can be initialized with m, n and a value of choice.
Or equivalently introduce the tensor type with variadic dimension arguments in initializers though you loose static type safety considering compatibility to tensor operations.

2 Likes