Elye
Elye

Reputation: 60081

List<List<Char>> + List<Char> = List<Any>?

I have a below code which works.

class ListManipulate(val list: List<Char>, val blockCount: Int) {

    val result: MutableList<List<Char>> = mutableListOf()

    fun permute(sequence: List<Int> = emptyList(), start: Int = 0, count: Int = blockCount) {
        if (count == 0) {
            result.add(constructSequence(sequence))
            return
        }
        for (i in start .. list.size - count) {
            permute(sequence + i, i + 1, count - 1)
        }
    }

    private fun constructSequence(sequence: List<Int>): List<Char> {
        var result = emptyList<Char>()
        for (i in sequence) {
            result += list[i]
        }
        return result
    }
}

However, when I change the result from MutableList to normal List, i.e.

    var result: List<List<Char>> = emptyList()
    // ...
            result += constructSequence(sequence)

I got this error Type mismatch. Require: List<List<Char>>; Found: List<Any>

The full code as below

class ListManipulate(val list: List<Char>, val blockCount: Int) {

    var result: List<List<Char>> = emptyList()

    fun permute(sequence: List<Int> = emptyList(), start: Int = 0, count: Int = blockCount) {
        if (count == 0) {
            result += constructSequence(sequence)
            return
        }
        for (i in start .. list.size - count) {
            permute(sequence + i, i + 1, count - 1)
        }
    }

    private fun constructSequence(sequence: List<Int>): List<Char> {
        var result = emptyList<Char>()
        for (i in sequence) {
            result += list[i]
        }
        return result
    }
}

Why result + constructSequence(sequence) would result in List<Any> instead of List<List<Char>>?

Is there a way I could still use the normal List> and not the mutable list?

Upvotes: 1

Views: 112

Answers (2)

leonardkraemer
leonardkraemer

Reputation: 6793

The problem is that += is overloaded. If it sees an Iterable, Array or Sequence it behaves differently. You have to explicitly use plusElement() to achieve the behaviour you intend.

Consider the following code.:

class ListManipulate(val list: List<Char>, val blockCount: Int) {

    var result: List<List<Char>> = emptyList()

    fun permute(sequence: List<Int> = emptyList(), start: Int = 0, count: Int = blockCount) {
        if (count == 0) {
            result = result.plusElement(constructSequence(sequence))
            return
        }
        for (i in start..list.size - count) {
            permute(sequence + i, i + 1, count - 1)
        }
    }

    private fun constructSequence(sequence: List<Int>): List<Char> =
        List(sequence.size, { i -> list[sequence[i]] })
}

PS: I also took the liberty to update your constructSequence() to something more concise.

Btw: += uses addAll internally.

/**
 * Returns a list containing all elements of the original collection and then all elements of the given [elements] collection.
 */
public operator fun <T> Collection<T>.plus(elements: Iterable<T>): List<T> {
    if (elements is Collection) {
        val result = ArrayList<T>(this.size + elements.size)
        result.addAll(this)
        result.addAll(elements)
        return result
    } else {
        val result = ArrayList<T>(this)
        result.addAll(elements)
        return result
    }
}

Side note: you can also do:

 result.toMutableList().add(constructSequence(sequence))

It is fine to return a MutableList, the only difference really is that the List interface doesnt have the manipulation methods. Internally both are represented by an ArrayList

@SinceKotlin("1.1")
@kotlin.internal.InlineOnly
public inline fun <T> List(size: Int, init: (index: Int) -> T): List<T> = MutableList(size, init)

Upvotes: 0

nhaarman
nhaarman

Reputation: 100388

CTRL + click on the + in IDEA, you'll see that it takes you to the following function:

/**
 * Returns a list containing all elements of the original collection and then all elements of the given [elements] collection.
 */
public operator fun <T> Collection<T>.plus(elements: Iterable<T>): List<T> {
    /* ... */
}

Which means that you add all the individual elements of elements to the receiver. That is, you'll add all T's to the List<List<T>>. Since List<T> is not T, you'll get List<Any> as a result.

Upvotes: 2

Related Questions