86

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?

Akshar Patel
  • 8,998
  • 6
  • 35
  • 50

12 Answers12

154

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
holi-java
  • 29,655
  • 7
  • 72
  • 83
  • 68
    This makes my head hurt if I try to think too hard about how that's actually working (despite not actually being complex). It does make sense when you read it though! – Michael Jul 28 '17 at 16:10
  • 25
    Yet another explanation in simple terms. This syntax is a shorthand for `a = b.also({ b = a })`. We see `also()` method being called on `b` with an argument of lambda `{ b = a }`. `also` just calls its parameter, then returns `this`. So first lambda `{ b = a }` is executed, then `a` is assigned the result of `also()`, which is its "this", which is the initial `b`. – Anton3 Aug 03 '18 at 17:53
  • 8
    Awesome! It works even for array elements: `a[i] = a[j].also { a[j] = a[i] }` – Peter Feb 01 '21 at 06:21
  • 3
    @Anton3 your explanation is the best I've seen yet, but something is still not clear to me. You say that the lambda is executed first. So that means b is assigned the value of a and b is therefore overwritten. There is some implicit buffering going on here. I decompiled the byte code for a = b.also { b = a } and got the following in Java, which is the traditional swap algorithm: int a = 1; int b = 2; byte var4 = b; b = a; a = var4; So when and where is the buffering of b conceptually occurring? Stack? Closure? – Phil Mar 22 '21 at 05:34
  • 3
    @Phil In Kotlin or Java, whenever an argument is passed to a function, or returned, or captured in a closure, a copy is created (In case of object types, we copy a reference to the GC-managed object). However, Kotlin has punched a small hole in that system with `var` captures, which are conceptually implemented by putting the value in a wrapper object that lets us mutate the inner value (which the variable names) as long as we have a reference to the wrapper object. – Anton3 Mar 23 '21 at 08:26
  • 3
    So, in the example, we first create the lambda object, which captures a copy of `a` and `var b` (which conceptually works by the mechanism described above). Second, we call the extension function `also`, passing it the lambda (conceptually, a copy of the reference to the lambda object, to be precise), as well as a copy of `b` as the hidden `this` parameter. Third, `also` calls the lambda, which mutates `var b`, then returns `this`, which is the initial value of `b`. – Anton3 Mar 23 '21 at 08:27
  • I say "conceptually", because with Kotlin's `inline` functions, it can optimize out both the lambda object and the wrapper object, but anyway, the only fact needed to answer the question is that `b` is copied when passed as `this` parameter. – Anton3 Mar 23 '21 at 08:34
  • This code is cool, but I wouldn't go so far as to say there is "no need" for a swap function "at all," given that this code is difficult for the casual observer to read and understand. @Lior's answer is more readable and therefore easier to modify the code around without introducing bugs. – LarsH Jun 16 '23 at 20:27
  • Incidentally, `apply` works just as well as `also` here, since the lambda makes no use of `this`. But `also` sounds a bit more like natural English... – LarsH Jun 16 '23 at 20:28
26

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.

Ruckus T-Boom
  • 4,566
  • 1
  • 28
  • 42
19

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.
Lior Bar-On
  • 10,784
  • 5
  • 34
  • 46
  • 3
    This is really just the generic Java way he was talking about. So you're answer is effectively "no, there is no better way". I think I'm inclined to agree. – Michael Jul 28 '17 at 16:18
16

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.

gavrie
  • 1,641
  • 1
  • 15
  • 14
12

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
Willi Mentzel
  • 27,862
  • 20
  • 113
  • 121
2

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
repitch
  • 1,858
  • 1
  • 15
  • 34
  • What's the point of introduction of another infix function? The `to` would do exactly the same `val (b0, b0) = a to b` – ruX Jan 03 '23 at 23:29
1

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)}

0

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] }
}
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

Er. Harsh Rathore
  • 758
  • 1
  • 7
  • 21
0

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
Kramer
  • 927
  • 2
  • 13
  • 30
-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]
    }
Nicolas Duponchel
  • 1,219
  • 10
  • 17
-2

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
    }
}
AppDeveloper
  • 1,816
  • 7
  • 24
  • 49
android developer
  • 114,585
  • 152
  • 739
  • 1,270