Any performance cost with using "when" instead of "if-else"? - kotlin

While developing, I've found that using when looks nicer a lot of the times compared to if-else. So, I'm curious whether using when instead of the typical if-else in simple situations have a performance impact (even if it's small). An example is doing:
val someNumber = when (someObject) {
null -> 0
else -> someCalculation()
}
versus
val someNumber = if (someObject == null) {
0
} else {
someCalculation()
}
Is there a performance difference between the two?

tl;dr: No you should not expect when to slow anything down.
The compiler reuses if/else, switch and ternary operator constructs in order to express when statements. Take this example:
fun whenApplication(someObject: String?) = when (someObject) {
null -> 0
else -> 2
}
And its bytecode shown as Java code:
public static final int whenApplication(#Nullable String someObject) {
return someObject == null ? 0 : 2;
}
Slightly more complex when-tests are shown here:
fun whenApplication(someObject: Any?) = when (someObject) {
is Int -> 2
in 0..2 -> 4
else -> 5
}
And the corresponding bytecode as Java:
public static final int whenApplication(#Nullable Object someObject) {
int var10000;
if (someObject instanceof Integer) {
var10000 = 2;
} else {
byte var2 = 0;
var10000 = CollectionsKt.contains((Iterable)(new IntRange(var2, 2)), someObject) ? 4 : 5;
}
return var10000;
}

Related

incrementing hash map count in Kotlin

I have the function below. However, when I pass a string to it, I get the following error:
error: operator call corresponds to a dot-qualified call 'charCountMap.get(c).plus(1)' which is not allowed on a nullable receiver 'charCountMap.get(c)'. charCountMap.put(c, charCountMap.get(c) + 1)
private fun characterCount(inputString:String) {
val charCountMap = HashMap<Char, Int>()
val strArray = inputString.toCharArray()
for (c in strArray)
{
if (charCountMap.containsKey(c))
{
charCountMap.put(c, charCountMap.get(c) + 1)
}
else
{
charCountMap.put(c, 1)
}
}
}
The Kotlin Standard Library has groupingBy and eachCount for this purpose, you don't need to do any of this manually:
private fun characterCount(inputString:String) {
val charCountMap : Map<Char, Int> = inputString.groupingBy { it }.eachCount()
}
Note that I put the type on charCountMap for clarity, but it can be left off and inferred.
There is nice compute method in HashMap for this:
private fun characterCount(inputString:String) = hashMapOf<Char, Int>().also { charCountMap ->
inputString.forEach { charCountMap.compute(it) { _, v -> if (v == null) 1 else v + 1 } }
}
Both the other answers are correct. Todd's answer is right, you don't need to write a function for this. Just use the standard library. And if you are going to write a function that updates maps, Михаил Нафталь's suggestion to use compute() to handle updating existing values is also good.
However, if you're just doing this an an exercise, here are three suggestions to fix/improve your algorithm:
Instead of get(), use getValue(), which does not return null. It will raise an exception if the element does not exist, but you already checked for that.
Use the [] operator instead of put() (no need to, it's just nicer syntax).
You don't need to call toCharArray() because Strings are already iterable.
if (charCountMap.containsKey(c))
{
charCountMap[c] = charCountMap.getValue(c) + 1
}
else
{
charCountMap[c] = 1
}
Rewriting the whole thing using standard formatting:
fun characterCount(inputString: String): Map<Char, Int> {
val charCountMap = mutableMapOf<Char, Int>()
for (c in inputString) {
if (charCountMap.containsKey(c)) {
charCountMap[c] = charCountMap.getValue(c) + 1
} else {
charCountMap[c] = 1
}
}
return charCountMap
}

Short circuiting operation in Kotlin sequences

I've been wondering if there is any chance of implementing sequence which executes some operation n times using map operator, but can terminate that execution during processing on the 'go'? Here is an imperative code which I'm trying to implement using Kotlin sequences:
val offers = mutableListOf<String>()
for (pageNumber in FIRST_PAGE_NUMBER until numberOfPages) {
val offersInPage = findByPage(query, pageSize, pageNumber)
offers.addAll(offersInPage)
if(offersInPage.size == 5)
break
}
The main thing is that I would like to hang up processing any further requests to external service when response from the previous one meets some conditions.
When trying to implement it in more declarative way I ended up with something like this:
IntArray(numberOfPages)
.asSequence()
.map { findByPage(query, pageSize, it) }
.takeWhile { it.size == 5 }
.flatten()
.toList()
But the findByQuery method is invoked n times and then the result is filtered. Is there any operator which help me implement something like terminating that lazy operation once given condition is met?
The sequence works exactly as you expected. It performs on map checks the takeWhile condition and does only continue if it is true.
One problem could be IntArray(numberOfPages). This creates an array of 0 of the size numberOfPages. So you iterate over a sequence of 0 and not the page numbers. You can simply change this to the for loop condition.
(FIRST_PAGE_NUMBER until numberOfPages)
.asSequence()
Another problem could be the takeWhile. In the for loop you stop after the first element with a size == 5. But in the sequence you stop before the first element with a size != 5. The simplest solution for this problem is to find another condition that would break the loop before offers.addAll(offersInPage) will be executed. If this is not possible you can use something like this:
fun <T> Sequence<T>.takeWhileEndInclusive(predicate: (T) -> Boolean) = object : Sequence<T> {
val sequence = this#takeWhileEndInclusive
override fun iterator() = object : Iterator<T> {
val iterator = sequence.iterator()
var nextState: Int = -1
var nextItem: T? = null
var found = false
private fun calcNext() {
if (!found && iterator.hasNext()) {
val item = iterator.next()
if (!predicate(item)) {
found = true
}
nextState = 1
nextItem = item
return
}
nextState = 0
}
override fun next(): T {
if (nextState == -1)
calcNext()
if (nextState == 0)
throw NoSuchElementException()
#Suppress("UNCHECKED_CAST")
val result = nextItem as T
nextItem = null
nextState = -1
return result
}
override fun hasNext(): Boolean {
if (nextState == -1)
calcNext()
return nextState == 1
}
}
}
This is a slightly adjusted version of the default takeWhile implementation.
Usage:
(FIRST_PAGE_NUMBER until numberOfPages)
.asSequence()
.map { findByPage(query, pageSize, it) }
.takeWhileEndInclusive { it.size != 5 }
.flatten()
.toList()

How and when does kotlin let run?

for all the examples on the internet i cant figure out when and how is kotlins let ran?
if(phones.size == 0){
phones.add("")
}
return phones[0]
so if phones list size is 0, we add empty string and return that instead.
Now how would one do same with let ?
phones.let {
return ""
}
does this work with size 0, or do i have to have null list?
do i need return keyword, if yes, where?
is the above fun always going to return empty string? or just when phones is null?
when is this let code block even ran?
Update:
val cakes = listOf("carrot", "cheese", "chocolate")
fun main(args: Array<String>) {
var cakesEaten = 0
while (cakesEaten < 3) { // 1
cakesEaten ++
val result = cakes?.let{
if(cakesEaten == 2) {
"HeyLo"
} else {
2
}
}
println("result value = $result")
when(result) {
is String -> println(" result variable is a String")
is Int -> println(" result variable is Integer")
}
}
}
result value = 2
result variable is Integer
result value = HeyLo
result variable is a String
result value = 2
result variable is Integer
Original post
If your 'phones' Object is a Nullable type,
val result = phones?.let{
// this block runs only if phones object is not null
// items can be accessed like it.size
// expression result will be returned. no need to mention return.
if(it.size == 0) {
it.add("")
it[0]
} else it.size
}
result value will be either it[0] or it.size and its type will be Any.
But if this the functionality you need you can check Markos solution.
If you're interested in how to write your logic in Kotlin's FP idiom, it doesn't involve let at all:
phones.takeIf { it.isEmpty() }?.add("")
return phones[0]
However, I don't find this idiom better than what you started out with.

Operator overloading Kotlin

I'm new to kotlin and I'm working on operators overloading for a custom class I defined. The class is called "Rational" and represents a rational number, like for example 117/1098. Class is defined as below and I have overloaded a bunch of operators, like plus, minus, times and so on. However I'm uncertain about what I have to do to overload "in" operator.
Here is my class:
data class Rational(val rational: String) {
private val numerator: BigInteger
private val denominator: BigInteger
init {
val splitted = rational.split("/")
numerator = splitted[0].toBigInteger()
denominator = when (splitted[1]) {
"0" -> throw Exception("not allowed")
else -> splitted[1].toBigInteger()
}
}
operator fun plus(number: Rational): Rational {
val gcm = denominator * number.denominator
val numerator = (gcm / denominator) * numerator + (gcm / number.denominator) * number.numerator
return Rational("$numerator/$gcm")
}
operator fun minus(number: Rational): Rational {
val gcm = denominator * number.denominator
val numerator = (gcm / denominator) * numerator - (gcm / number.denominator) * number.numerator
return Rational("$numerator/$gcm")
}
operator fun times(number: Rational): Rational {
val numerator = numerator * number.numerator
val denominator = denominator * number.denominator
return Rational("$numerator/$denominator")
}
operator fun div(number: Rational): Rational {
val numerator = numerator * number.denominator
val denominator = denominator * number.numerator
return Rational("$numerator/$denominator")
}
operator fun compareTo(number: Rational): Int {
val ratio = this.numerator.toFloat() / this.denominator.toFloat()
val numberRatio = number.numerator.toFloat() / number.denominator.toFloat()
if (ratio > numberRatio) {
return 1
} else if (ratio == numberRatio) {
return 0
}
return -1
}
operator fun unaryMinus(): Rational {
val inverseNumerator = -numerator
return Rational("$inverseNumerator/$denominator")
}
operator fun unaryPlus(): Rational {
return Rational("$numerator/$denominator")
}
operator fun rangeTo(end: Rational): Any {
var range: MutableList<Rational> = arrayListOf()
val startNumerator = this.numerator.toInt()
val endNumerator = end.numerator.toInt()
var index = 0
if (this.denominator == end.denominator) {
for (i in startNumerator..endNumerator) {
range.add(index, Rational("$i/$denominator"))
}
}
return range
}
operator fun contains(number: Rational): Boolean {
if (this.denominator % number.denominator == 0.toBigInteger()
&& this.numerator <= number.numerator) {
return true
}
return false
}
override fun toString(): String {
val gcd = numerator.gcd(denominator)
return if (gcd != null) {
val newNumerator = numerator / gcd
val newDenominator = denominator / gcd
"$newNumerator/$newDenominator"
} else {
"$numerator/$denominator"
}
}
}
infix fun Int.divBy(denominator: Int): Rational {
if (denominator == 0) {
throw Exception("denominator 0 not allowed")
}
return Rational("$this/$denominator")
}
infix fun Long.divBy(denominator: Long): Rational {
if (denominator == 0L) {
throw Exception("denominator 0 not allowed")
}
return Rational("$this/$denominator")
}
infix fun BigInteger.divBy(denominator: BigInteger): Rational {
if (denominator == 0.toBigInteger()) {
throw Exception("denominator 0 not allowed")
}
return Rational("$this/$denominator")
}
fun String.toRational(): Rational {
return Rational(this)
}
And here is my main body that obviously still doesn't compile:
fun main() {
val half = 1 divBy 2
val third = 1 divBy 3
val twoThirds = 2 divBy 3
println(half in third..twoThirds) // this line does not compile beacause in operator is not defined for the class
}
I guess I have to override "rangeTo" operator but I'm uncertain about the operator prototype. I there somebody that can please help me to get to the right track?
The way to make in work is for the third..twoThirds call to return something that has a contains(Rational) method, which is what the in call translates to.
One way to do this is to return a ClosedRange<Rational> here, like so:
operator fun rangeTo(end: Rational): ClosedRange<Rational> {
return object : ClosedRange<Rational> {
override val endInclusive: Rational = end
override val start: Rational = this#Rational
}
}
This puts a type constraint on Rational, as a ClosedRange needs a Comparable implementation to be able to determine whether a value belongs in it. You can do this by implementing the Comparable interface, and then adding operator to your existing compareTo operator (plus it's a good practice to rename the parameter to match the interface):
data class Rational(val rational: String) : Comparable<Rational> {
...
override operator fun compareTo(other: Rational): Int {
val ratio = this.numerator.toFloat() / this.denominator.toFloat()
val numberRatio = other.numerator.toFloat() / other.denominator.toFloat()
if (ratio > numberRatio) {
return 1
} else if (ratio == numberRatio) {
return 0
}
return -1
}
}
You could also avoid the conversion to floats entirely by using this implementation instead, as suggested in the comment below by #gidds:
override operator fun compareTo(other: Rational): Int {
return (numerator * other.denominator - denominator * other.numerator).signum()
}
Also, your current contains implementation could probably be discarded, as you no longer need it, and it functions rather oddly.
To add something other than the direct answer here: as #Eugene Petrenko suggested in their answer, it would be practical to add a couple constructors other than the one that uses a String, for example one that takes two Ints, and one that takes two BigIntegerss.
The in operator is declared inverse. You need an extension function on the right side that takes the left side.
https://kotlinlang.org/docs/reference/operator-overloading.html#in
You miss an infix function divBy to allow turing Int into Rational, e.g.
infix fun Int.divBy(i: Int) = Rational("$this/$i")
Not the code like val half = 1 divBy 2 will work. Theoretically, it may make sense to add a constructor for Rational from Ints to avoid parsing.
There is an incorrect return type in rangeTo method in the Rational class, it should not be Any. It should be declared as
data class RationalRange(val left: Rational, val right: Rational) {
operator fun contains(r: Rational) = left <= r && r <= right
}
operator fun rangeTo(end: Rational): RationalRange(this, end)
Now the example with x in a..b should work.
UPD: added the RationalRange. I missed the point, sorry. You do not need contains function implemented for the Rational class at all.
The compareTo function of Rational is unlikely to use .toFloat() instead, you may implement that directly with integer numbers
A straightforward solution is to implement the Comparable Interface in your class.
data class Rational(val rational: String) : Comparable<Rational>
Then implement the compareTo() function, with your comparison logic.
override fun compareTo(other: Rational): Int {
//Normalize the numerators of each rational
val thisNumerator = this.numerator * other.denominator
val otherNumerator = other.numerator * this.denominator
//Then compare them
return when{
thisNumerator > otherNumerator -> 1
thisNumerator < otherNumerator -> -1
else -> 0
}
}
This will resolve the compile error without you needing to override the rangeTo() function with custom logic.

Swift 'if let' statement equivalent in Kotlin

In Kotlin is there an equivalent to the Swift code below?
if let a = b.val {
} else {
}
You can use the let-function like this:
val a = b?.let {
// If b is not null.
} ?: run {
// If b is null.
}
Note that you need to call the run function only if you need a block of code. You can remove the run-block if you only have a oneliner after the elvis-operator (?:).
Be aware that the run block will be evaluated either if b is null, or if the let-block evaluates to null.
Because of this, you usually want just an if expression.
val a = if (b == null) {
// ...
} else {
// ...
}
In this case, the else-block will only be evaluated if b is not null.
Let's first ensure we understand the semantics of the provided Swift idiom:
if let a = <expr> {
// then-block
}
else {
// else-block
}
It means this: "if the <expr> results in a non-nil optional, enter the then-block with the symbol a bound to the unwrapped value. Otherwise enter the else block.
Especially note that a is bound only within the then-block. In Kotlin you can easily get this by calling
<expr>?.also { a ->
// then-block
}
and you can add an else-block like this:
<expr>?.also { a ->
// then-block
} ?: run {
// else-block
}
This results in the same semantics as the Swift idiom.
My answer is totally a copy cat from the others. However, I cannot understand their expression easily. So I guess it would be nice to provide an more understandable answer.
In swift:
if let a = b.val {
//use "a" as unwrapped
}
else {
}
In Kotlin:
b.val?.let{a ->
//use "a" as unwrapped
} ?: run{
//else case
}
if let statement
Swift if let Optional Binding (so called if-let statement) is used to extract a non-optional value if one exists, or to do nothing if a value is nil.
Swift's if-let statement:
let b: Int? = 50
if let a: Int = b {
print("Good news!")
} else {
print("Equal to 'nil' or not set")
}
/* RESULT: Good news! */
In Kotlin, like in Swift, to avoid crashes caused by trying to access a null value when it’s not expected, a specific syntax (like b.let { } in second example) is provided for properly unwrapping nullable types:
Kotlin's equivalent1 of Swift's if-let statement:
val b: Int? = null
val a = b
if (a != null) {
println("Good news!")
} else {
println("Equal to 'null' or not set")
}
/* RESULT: Equal to 'null' or not set */
Kotlin’s let method, when used in combination with the safe-call operator ?:, provides a concise way to handle nullable expressions.
Kotlin's inline let function and Elvis Operator of Swift's nil coalescing operator:
val b: Int? = null
val a = b.let { nonNullable -> nonNullable } ?: "Equal to 'null' or not set"
println(a)
/* RESULT: Equal to 'null' or not set */
guard let statement
guard-let statement in Swift is simple and powerful. It checks for some condition and if it evaluates to be false, then the else statement executes which normally will exit a method.
Let's explore a Swift's guard-let statement:
let b: Int? = nil
func method() {
guard let a: Int = b else {
print("Equal to 'nil' or not set")
return
}
print("Good news!")
}
method()
/* RESULT: Equal to 'nil' or not set */
Kotlin's similar effect of Swift's guard-let statement:
Unlike Swift, in Kotlin, there is no guard statement at all. However, you can use the Elvis Operator – ?: for getting a similar effect.
val b: Int? = 50
fun method() {
val a = b ?: return println("Equal to 'null' or not set")
return println("Good news!")
}
method()
/* RESULT: Good news! */
there are two answers above, both got a lot acceptances:
str?.let{ } ?: run { }
str?.also{ } ?: run { }
Both seem to work in most of the usages, but #1 would fail in the following test:
#2 seems better.
Unlike Swift, Its not necessary to unwrap the optional before using it in Kotlin. We could just check if the value is non null and the compiler tracks the information about the check you performed and allows to use it as unwrapped.
In Swift:
if let a = b.val {
//use "a" as unwrapped
} else {
}
In Kotlin:
if b.val != null {
//use "b.val" as unwrapped
} else {
}
Refer Documentation: (null-safety) for more such use cases
Here's how to only execute code when name is not null:
var name: String? = null
name?.let { nameUnwrapp ->
println(nameUnwrapp) // not printed because name was null
}
name = "Alex"
name?.let { nameUnwrapp ->
println(nameUnwrapp) // printed "Alex"
}
Here's my variant, limited to the very common "if not null" case.
First of all, define this somewhere:
inline fun <T> ifNotNull(obj: T?, block: (T) -> Unit) {
if (obj != null) {
block(obj)
}
}
It should probably be internal, to avoid conflicts.
Now, convert this Swift code:
if let item = obj.item {
doSomething(item)
}
To this Kotlin code:
ifNotNull(obj.item) { item ->
doSomething(item)
}
Note that as always with blocks in Kotlin, you can drop the argument and use it:
ifNotNull(obj.item) {
doSomething(it)
}
But if the block is more than 1-2 lines, it's probably best to be explicit.
This is as similar to Swift as I could find.
There is a similar way in kotlin to achieve Swift's style if-let
if (val a = b) {
a.doFirst()
a.doSecond()
}
You can also assigned multiple nullable values
if (val name = nullableName, val age = nullableAge) {
doSomething(name, age)
}
This kind of approach will be more suitable if the nullable values is used for more than 1 times. In my opinion, it helps from the performance aspect because the nullable value will be checked only once.
source: Kotlin Discussion
I'm adding this answer to clarify the accepted answer because it's too big for a comment.
The general pattern here is that you can use any combination of the Scope Functions available in Kotlin separated by the Elvis Operator like this:
<nullable>?.<scope function> {
// code if not null
} :? <scope function> {
// code if null
}
For example:
val gradedStudent = student?.apply {
grade = newGrade
} :? with(newGrade) {
Student().apply { grade = newGrade }
}
The cleanest option in my opinion is this
Swift:
if let a = b.val {
} else {
}
Kotlin
b.val.also { a ->
} ?: run {
}
Swift if let statement in Kotlin
The short answer is use simple IF-ELSE as by the time of this comment there is no equivalent in Kotlin LET,
if(A.isNull()){
// A is null
}else{
// A is not null
}
we can get the same Unwraping syntax like Swift if let using inline fun
inline fun <T:Any?> T?.unwrap(callback: (T)-> Unit) : Boolean {
return if (this != null) {
this?.let(callback)
true
}else {
false
}
}
Uses: :
val name : String? = null
val rollNo : String? = ""
var namesList: ArrayList<String>? = null
if (name.unwrap { name ->
Log.i("Dhiru", "Name have value on it $name")
})else if ( rollNo.unwrap {
Log.i("Dhiru","Roll have value on it")
}) else if (namesList.unwrap { namesList ->
Log.i("Dhiru","This is Called when names list have value ")
}) {
Log.i("Dhiru","No Field have value on it ")
}
The problem with the Any?.let {} ?: run {} constructions is that:
It only allows for one non-null check per statement
If the let block returns null the run block is evaluated anyway
It's not possible to perform multiple checks in a switch/when style
A solution which tackles most of these problems is to define functions like the following:
private inline fun <A> ifNotNull(p1: A?, block: (A) -> Unit): Unit? {
if (p1 != null) {
return block.invoke(p1)
}
return null
}
private inline fun <A, B> ifNotNull(p1: A?, p2: B?, block: (A, B) -> Unit): Unit? {
if (p1 != null && p2 != null) {
return block.invoke(p1, p2)
}
return null
}
private inline fun <A, B, C> ifNotNull(p1: A?, p2: B?, p3: C?, block: (A, B, C) -> Unit): Unit? {
if (p1 != null && p2 != null && p3 != null) {
return block.invoke(p1, p2, p3)
}
return null
}
This would allow for a statement like:
ifNotNull(a, b) { a, b ->
// code when a, b are not null
} ?:
ifNotNull(c) { c ->
// code when a, b are null and c not null
} ?:
ifNotNull(d, e, f) { d, e, f ->
// code when a, b, c are null and d, e, f not null
} ?: run {
// code which should be performed if a, b, c, d, e and f are null
}
The only caveat is that continue and break statements are not supported if executed within a loop compared to Swift's if let equivalent.
Probably I am very late however the easiest way to unwrap and option is
yourOptionalString ?: return
this was all the following lines will have unwrapped string
If b is a member variable then this approach seems most readable to me:
val b = this.b
if (b == null) {
return
}
println("non nullable : ${b}")
This is also consistent with how it works in swift, where a new local variable shadows the member variable.