Contains array of arrays


#1

Hi, I was trying this piece of code:

val itemA = arrayOf(0,0)
val itemB = arrayOf(1,1)
val items = arrayOf(arrayOf(1,1), arrayOf(2,2), arrayOf(3,3))
assertFalse(itemA in items)
assertTrue(itemB in items)

And I thought it would pass, but the last statement is not true. Is this to be expected?
Thanks for your help


#2

In fact the first statement is false for a bad reason.

itemA in items

means in fact :

items.contains(itemA)

If you read the code of Collection.contains() you will see that contains use the equals() comparison (== in kotlin). But array equality in java and kotlin is reference equality.

You have to solutions : the first one is to use list instead of array, because it’s override the equals() comparison, the second one is to define an infix function like :

infix fun <T> Array<T>.isIn(array: Array<Array<T>>): Boolean {
    return array.any { it.contentEquals(this) }
}

val itemA = arrayOf(0,0)
val itemB = arrayOf(1,1)
val items = arrayOf(arrayOf(1,1), arrayOf(2,2), arrayOf(3,3))
assertFalse(itemA isIn items)
assertTrue(itemB isIn items)

#3

I see, this is what I got wrong. I was somehow thinking that Kotlin would do an equality instead of an identity (reference) equality, just like it happens for Strings.

Thank you very much for your answer and the handy function.


#4

Content equality for arbitrary array is impossible to do since there is universal way to compare objects inside the array. Even for primitive arrays, introducing content equality checks could create a runtime performance overhead, since you need to compare all of the elements.


#5

@darksnake I understand it might not be good for performance reasons, but that does not make the fact any better. Arrays seem to be presented as basic types in Kotlin so one might expect that in a comparison an equality check is done. I think one solution would be to show a compiler warning similar as it happens when one tries to do arrayOf(1,1) == arrayOf(1,1)


#6

I won’t argue about warning, warnings are always good, but it is more or less obvious that generic array can’t provide content equality. The same way it won’t work for collections. In theory it is possible to provide content equality for primitive arrays like IntArray, but I am not sure it is worth it.


#7

I do not see why it is obvious that it can’t. It is obvious that it can’t if you read the code of the Kotlin libraries, yes. But if Kotlin libraries had something like this:
fun <T> Array<T>.equals(otherArray: Array<T>) = contentEquals(otherArray)

I think it would be possible to use the in operator using generics. Whenever the Generic object has implemented the equals method, an equality comparison using its equals method will be done, and if not an identity comparison will be done. That would automatically cover equality for primitive arrays


#8

Arrays were designed in java. I think Kotlin follows the rules of java for a better compatibility, but yeah equality is not perfect, it could be complicated if your Java code follows some rules and the same kotlin converted code follows over convention. Just be carefully when you use == in kotlin or in, try to know if the operator equals is defined for the object. Note that if you try == between arrays the kotlin plugin will warn you that you should use contentEquals instead.


#9

You see, sometimes Kotlin follows the rules of Java and sometimes it does not. For Strings, equality comparison is done by default and that is a good thing, and an improvement from Java. But the case of arrays there is no improvement over this issue. That is precisely why a warning would be a good compromise, just like in the case you just mentioned.


#10

No, read the documentation of Kotlin. When you use a == b operator, the code is translated to a?.equals(b) ?: (b === null) (source : https://kotlinlang.org/docs/reference/operator-overloading.html)

So when you use :

"foo" == "bar"

In fact you write the java equivalent code :

"foo" != null ? "foo".equals("bar") : "bar" == null;

And the equality comparison is overrided in Java, so “hello” == “hello” but “hello” !== “hello” (reference comparison)

The equivalent operator for == Java is the === Kotlin operator.
Kotlin == means Java equals()
Kotlin === means Java ==


#11

True… I have forgotten about that. That was a very smart way to redefine the == operator which works for Strings correctly because there was an implementation of equals already in place, whereas for arrays the equals implementation does not exist. So the conclusion is that arrays suffer from exactly the same problems as in Java because they are the same thing, which could or could not be a problem depending on the point of view. Thanks for your clear explanations.


#12

Just avoid arrays, there’s little reason to use them (as in Java). Use lists, they are backed by arrays internally, so performance hit is very little if any and are easier to use.


#13

It is true for generic arrays, but not for primitive arrays. In Kotlin, primitive arrays have their own separate classes (which is very good), and all the talk is really about them.

By the way, project Valhalla is on its way and it should bring value types to JVM therefore allowing to treat some types as value types (I think in Kotlin those will be data classes) and use specialized arrays for them.


#14

@vbezhenar Yes, that is sort of the conclusion I came to. It is not worth the hassle as the benefits in performance are not significant.

@darksnake That would be great, it is good to know things are evolving. Kotlin is already a step up from Java, and it keeps improving. Coroutines is one of the great things I have been waiting to come out of the experimental phase.


#15

I hope there will be a distinction between data classes and value types, as you would want to use value types for values which are not too big (in Bytes). I think adding a new keyword for value types would be better than just adding it to the data class keyword.

Yeah, I know this is off context :slight_smile: