ByteArray Comparison


#1

Hi all,

In Kotlin == is determined as structural equality, and === is reference equality. Why, for ByteArray does the following then fail given the statement above?

Assert.assertTrue(“abc”.toByteArray() == “abc”.toByteArray())

Follow up question is: In coming from Java I immediately revert back to the Arrays.equals method; what is the appropriate Kotlin way of doing this.

Thanks,
Gawie


Operations on Map fails when using ByteArray as key
#2

This is handled by kotlin.jvm.internal.Intrinsics#areEqual

public static boolean areEqual(Object first, Object second) {
    return first == null ? second == null : first.equals(second);
}

This feels inconsistent:
arrayOf(1, 2, 3) == arrayOf(1, 2, 3) is false
listOf(1, 2, 3) == listOf(1, 2, 3) is ```true````

Even though it is not idiomatic, you could still call Arrays.equals(arrayOf(1, 2, 3), arrayOf(1, 2, 3)).


#3

Thanks @Dittert for the reply. I know in most other languages that array equality is hardly ever done through simple == comparison, but given the Kotlin copied statement of the intent of == vs === it definitely feels as if testing array equality through equals should yield the correct result; especially since Kotlin make special provision for these types by type name.
It took Java and C (nearly) decades to join the party on String equality - why not do the same for arrays?


#4

No need t convince me: As I wrote, the difference between == on lists and arrays feels inconsistent to me as well.


#5

You know what else is missing from Kotlin as one of the newest languages on the block. Natural BigDecimal handling. Yes I said it :slight_smile: I know it is a niche item probably confined to the financial space, but handling BigDecimal in code really makes numeric operations look very unnatural (Java even more so).

The net result of this is that I have about 100 extension and infix methods just for BigDecimal - and even though it get much better - it is still not really natural math, comparison, initialization etc.

As anybody that has done this type of app will concur, if you do decimals - you do it a lot - and Java especially (more so than Kotlin) makes this kind of data manipulation really ugly. And to further rub salt in the wounds… Kotlin (over Java) has tremendous advantages to developing financial backends…


#6

Kotlin arrays are represented in runtime as java arrays (T[]), thus we can’t change the way their equals and hashCode methods are implemented.


#7

Could you share your extensions and tell what language support do you expect for BigDecimal from Kotlin?


#8

Hi @ilya.gorbunov

Pity about the arrays. I was at least hoping not to revert to the Java Array.equals but have a Kotlin specific comparison method (if == magic could not be performed):

Here is an excerpt of my very simple extensions:

infix fun Int?.eq(that: BigDecimal?) = this.toDec() eq that
infix fun Int?.lt(that: BigDecimal?) = this.toDec() lt that
infix fun Int?.gt(that: BigDecimal?) = this.toDec() gt that
infix fun Int?.ltEq(that: BigDecimal?) = this.toDec() ltEq that
infix fun Int?.gtEq(that: BigDecimal?) = this.toDec() gtEq that
infix fun String?.eq(that: BigDecimal?) = this.toDec() eq that
infix fun String?.lt(that: BigDecimal?) = this.toDec() lt that
infix fun String?.gt(that: BigDecimal?) = this.toDec() gt that
infix fun String?.ltEq(that: BigDecimal?) = this.toDec() ltEq that
infix fun String?.gtEq(that: BigDecimal?) = this.toDec() gtEq that

infix fun Long?.eq(that: BigDecimal?) = this.toDec() eq that
infix fun Long?.lt(that: BigDecimal?) = this.toDec() lt that
infix fun Long?.gt(that: BigDecimal?) = this.toDec() gt that
infix fun Long?.ltEq(that: BigDecimal?) = this.toDec() ltEq that
infix fun Long?.gtEq(that: BigDecimal?) = this.toDec() gtEq that

infix fun BigDecimal?.ltEq(that: BigDecimal?) : Boolean {
if (this == null && that == null)
return false

if (this == null || that == null)
    return false

return this.compareTo(that) <= 0

}

fun BigDecimal?.between(lower: BigDecimal?, upper: BigDecimal?) : Boolean {
if (this == null && lower == null && upper == null)
return true

if (this == null)
    return false

if (lower != null)
    if (this lt lower)
        return false

if (upper != null)
    if (this gt upper)
        return false

return true

}

fun BigDecimal?.notBetween(lower: BigDecimal?, upper: BigDecimal?) = !this.between(lower,upper)

fun String?.toDec() : BigDecimal? {
if (this == null)
return null

return BigDecimal(this)

}

A horde of other casting and comparison functions - similar to the examples above.
Which give me the chance to shorten code and for me, make it more readable than compareTo() e.g.:

object Test {
@JvmStatic
fun main(args : Array) {
val myDec = “1234.75”.toDec()
if (“1234.56” lt myDec && “1234.75” ne myDec)
println(“Case 1”)
else
println(“Case 2”)
}
}

A far out suggestion… is to handle decimals like C# maybe? The developer distraction and effort in C# decimals really is admirable. They handle like with L for Long M for Decimal (if I remember correctly); this while mathematics with these are natural across most types. Part of the niceness in the flow is that you don’t new it, which makes it feel like a primitive without the extra verbosity of creating new instances etc.
PS: Scala implicits used to drive me up the wall - but due to the lack of implicit casting in Kotlin one must find alternate ways to shorten and make better null-safety to types such as BigDecimal.


#9

It looks like that what you’re really missing is BigDecimal literals, and you wouldn’t need all these extensions if you were able to use literals in first place.

There is a related issue in our tracker: https://youtrack.jetbrains.com/issue/KT-11327.


#10

@ilya.gorbunov

Exactly! Or at least a good start. The thread mentioned however does not take it the extra step regarding comparison and mathematics. But I guess baby steps.


#11

I think a bunch of such extensions would form a nice kotlinx library.


#12

Hi @voddan,

Yes and no in my opinion. There are certain things that you simply cannot do which are required to make Kotlin more friendly. I also, for obvious things like e.g. comparison really don’t like these extension libraries (I have a few - like we all do I guess). The more really common code you carry - the worse your dependencies become between multiple projects. These libraries tend to host a lot of detail under the covers - and always leave you weary when you check out that project of 2 years ago trying to compile against your latest extensions library (unless you have a pot of money to write every conceivable test case there will always be side effects).

That said - one would also like for Kotlin not to be a huge dependency (megabytes upon megabytes of jars) and stay true to its objectives. As stated previously however - BigDecimal is a large part of many a developer’s life - and it really feels very alien in a Kotlin and (even more so) Java world.


#13

That is exactly my point. Kotlinx libs are shipped separately afaik


#14

A bit tangential, but why not use operator overloading for comparison operations in this case?
https://kotlinlang.org/docs/reference/operator-overloading.html


#15

@norswap You are correct… The only issue is in nullable cases. The rest is taken care of automatically by the BigDecimal.compareTo