Как можно объявить вложенные списки в Котлине? Я ищу что-то в виде:

var nestedList:List = [1,[2,[3,null,4]],[null],5]

Так что я могу сгладить его позже (результат должен быть nestedList = [1, 2, 3, 4, 5]).

6
Dan Crisan 23 Окт 2018 в 11:26

2 ответа

Лучший ответ

Если у вас есть структура вложенных массивов (например, val array: Array<Array<out Int?>> = arrayOf(arrayOf(1), arrayOf(2), arrayOf(3, null, 4))), вы можете просто использовать метод расширения flatten:

println(array.flatten().filterNotNull())
2
Andrey Ilyunin 23 Окт 2018 в 08:42

Все общие коллекции не могут поддерживать переменное количество слоев, поэтому с ними можно сделать только что-то вроде того, что написал Андрей Ильюнин - val array: Array<Array<out Int?>>.

Но я написал структуру классов, чтобы помочь вам в вашей цели. Это не другая коллекция, и вы не можете работать с ней как есть, но она может создавать любое количество слоев, которое вы хотите. Это совершенно общий вариант, поэтому вы можете поместить туда не только Int.

Прежде всего, мы начнем с класса NestedArrayItem, который представляет отдельный элемент или еще один вложенный массив:

class NestedArrayItem<T> {
    private val array: ArrayList<NestedArrayItem<T>>?
    private val singleItem: T?
    constructor(array: ArrayList<NestedArrayItem<T>>) {
        this.array = array
        singleItem = null
    }
    constructor(singleItem: T?) {
        this.singleItem = singleItem
        array = null
    }
    fun asSequence(): Sequence<T?> =
        array?.asSequence()?.flatMap { it.asSequence() } ?:
            sequenceOf(singleItem)
    override fun toString() =
        array?.joinToString(prefix = "[", postfix = "]") ?:
            singleItem?.toString() ?: "null"
}

Затем класс NestedArray, который похож на контейнер верхнего уровня для всех слоев:

class NestedArray<T> {
    private val array: ArrayList<NestedArrayItem<T>> = arrayListOf()

    fun add(value: T?) {
        array.add(NestedArrayItem(value))
    }

    fun addNested(nestedArray: NestedArray<T>) {
        array.add(NestedArrayItem(nestedArray.array))
    }

    fun flatten(): ArrayList<T?> = array.asSequence()
        .flatMap { it.asSequence() }
        .toCollection(arrayListOf())

    override fun toString() = array.joinToString(prefix = "[", postfix = "]")
}

И чтобы упростить запись значений, я дополнительно написал для этого класс построителя:

class NestedArrayBuilder<T> private constructor(private val result: NestedArray<T>){
    constructor(fillNestedBuilder: NestedArrayBuilder<T>.() -> Unit) : this(NestedArray()) {
        NestedArrayBuilder(result).apply(fillNestedBuilder)
    }
    fun add(value: T?): NestedArrayBuilder<T> {
        result.add(value)
        return this
    }
    fun addArray(fillNestedBuilder: NestedArrayBuilder<T>.() -> Unit): NestedArrayBuilder<T> {
        val nestedResult = NestedArray<T>()
        val nestedArray = NestedArrayBuilder(nestedResult).apply(fillNestedBuilder)
            .build()
        result.addNested(nestedArray)
        return this
    }

    fun build() = result
}

Это оно! Вы можете использовать это. Я привел пример того, как его использовать:

val array = NestedArrayBuilder<Int> {
    add(1)
    addArray {
        add(2)
        addArray {
            add(3)
            add(null)
            add(4)
        }
    }
    addArray {
        add(null)
    }
    add(5)
}.build()
assertEquals("[1, [2, [3, null, 4]], [null], 5]", array.toString())
assertEquals(arrayListOf(1, 2, 3, null, 4, null, 5), array.flatten())
1
Ircover 23 Дек 2019 в 14:53
52944429