Swap Function in Kotlin

is there any better way to write generic swap function in kotlin other than java way described in How to write a basic swap function in Java.

Is there any kotlin language feature which can make generic swap function more concise and intuitive?

33363 次浏览

No need a swap function in Kotlin at all. you can use the existing also function, for example:

var a = 1
var b = 2


a = b.also { b = a }


println(a) // print 2
println(b) // print 1

Edit: Thanks to @hotkey for his comment

I believe the code for swapping two variables is simple enough - not to try simplifying it any further.

The most elegant form of implementation IMHO is:

var a = 1
var b = 2


run { val temp = a; a = b; b = temp }


println(a) // print 2
println(b) // print 1

Benefits:

  • The intent is loud and clear. nobody would misunderstand this.
  • temp will not remain in the scope.

If you want to write some really scary code, you could have a function like this:

inline operator fun <T> T.invoke(dummy: () -> Unit): T {
dummy()
return this
}

That would allow you to write code like this

a = b { b = a }

Note that I do NOT recommend this. Just showing it's possible.

Kotlin encourages the use of immutable data when possible (such as using val instead of var). This greatly reduces the change for subtle bugs, since it's possible to reason more soundly about code if values don't change.

Swapping two values is very much the opposite of immutable data: Did I mean the value of a before or after the swap?

Consider rewriting your code in the following immutable way:

val a = 1
val b = 2


val (a2, b2) = b to a

This works by making use of destructuring declarations, along with the built-in to extension function that creates a Pair.

That is a good usage for with:

var a = 1
var b = 2


with(a) {
a = b
b = this
}


println(a) // 2
println(b) // 1

In order to use Kotlin List you could create this kind of extension. It returns a copy of this list with elements at indices a and b swapped.

fun <T> List<T>.swap(a: Int, b: Int): List<T> = this
.toMutableList()
.also {
it[a] = this[b]
it[b] = this[a]
}

Very simple, fast and elegant solution:

var a = 1
var b = 2
val (b0, a0) = a swap b
a = a0
b = b0


infix fun <A> A.swap(second: A): Pair<A, A> = second to this

prefer a=b.apply {b=a} for swapping the elements. If we want to perform some operation on the variable inside the lambda, then go for a = b.also {someFun(it)}

If you use an array, you can use this:

fun <T> Array<T>.swap(i: Int, j: Int) {
with(this[i]) {
this@swap[i] = this@swap[j]
this@swap[j] = this
}
}

If you're swapping array values in place, from a code readability perspective, it was helpful for me to add an extension function swapInPlace

fun <T> Array<T>.swapInPlace(i1: Int, i2: Int){
this[i1] = this[i2].also{ this[i2] = this[i1] }
}


fun main(){
val numbers = arrayOf(2, 1)


//This is easier for me to read...
numbers.swapInPlace(0, 1)


//Compared to this
numbers[0] = numbers[1].also{ numbers[1] = numbers[0] }
}

I have something interesting for all:

Why just numbers. We can swap anything with a generic class and a generic function

class Mutable<T>(var value: T) {
override fun toString() = value.toString()


/**
infix fun swapWith(other: Mutable<T>) {
value = other.value.also { other.value = value }
}
**/
}


fun <T> swap(num1: Mutable<T>, num2: Mutable<T>) {
num1.value = num2.value.also { num2.value = num1.value }
}


fun main() {
val num1 = Mutable(4)
val num2 = Mutable(6)


println("Before Swapping:-\n\tNumber#1 is: $num1\n\tNumber#2 is: $num2\n")


//calling way of class method is not like usual swap function
//num1 swapWith num2


//calling the actual swap function.
swap(num1, num2)


println("After Swapping:-\n\tNumber#1 is: $num1\n\tNumber#2 is: $num2\n")
}
  • class Mutable is a generic class here which can contain any type of data into it.

I overridden toString() method to directly accessing the value attribute by just calling the object.

  • fun swap is a true swap function for kotlin that gives you the call by reference's demo too.

  • operator swapWith also works as swap function, which is a part of Mutable class. I have commented that part because the calling way for the operator is not like the way we are used to with.

Output:

Before Swapping:-
Number#1 is: 4
Number#2 is: 6


After Swapping:-
Number#1 is: 6
Number#2 is: 4


I have different approach.

You can keep your two values in a Pair. Then you can do this:

fun <T> swap(pair: Pair<T, T>): Pair<T, T> {
return Pair(pair.second, pair.first)
}

and you use it like this:

var pairOfInts = Pair(1, 2)
println("first: ${pairOfInts.first}") // prints 1
println("second: ${pairOfInts.second}") // prints 2


pairOfInts = swap(pairOfInts)


println("first: ${pairOfInts.first}") //prints 2
println("second: ${pairOfInts.second}") //prints 1