Как создать общий массив c со значениями Nullable в kotlin - PullRequest
0 голосов
/ 05 марта 2020

Хорошо, поэтому, чтобы попытаться выучить язык kotlin, я пытаюсь реализовать кучу.

class Heap <T : Comparable<T>>(private var heap: Array<T>, private var size: Int){

У меня возникли некоторые трудности с тем, как ведут себя обнуляемые типы. В начале я дал массив элементов, но у меня возникли трудности, когда я хотел выделить больше элементов

private fun increaseSize(){
    if(size == heap.size){
        //okay this is garbage
        var enlargedHeap = arrayOfNulls<Comparable<*>>(heap.size * 2) as Array<T> 
        System.arraycopy(heap, 0, enlargedHeap, 0, size)
        heap = enlargedHeap
    }
}

Так что здесь я предполагаю, что я меняю кучу с Array<T> на Array<T?>, что имеет смысл. Поэтому я также изменил конструктор, взяв Array<T?>, и он предлагает некоторые довольно интересные вещи, где бы я ни пытался получить доступ heap[x]

else heap[pos1].compareTo(heap[pos2]) < 0

к

else heap[pos2]?.let { heap[pos1]?.compareTo(it) }!! < 0

, тогда я посмотрел во всплывающей подсказке о том, что arrayOfNulls<Comparable<*>>(heap.size * 2) as Array<T> вернул Array<T>

Но при выполнении arrayOfNulls он действительно возвращает массив с нулевыми значениями. Однако я получаю сообщение об ошибке, если я пытаюсь heap[x] = null

заявить

Нуль не может быть значением ненулевого типа T

Если я измените конструктор на Array<T?>, тогда входные массивы также должны явно обнуляться.

Что я исправил с помощью

class Heap <T : Comparable<T>>{

private var heap: Array<T?>
private var size = 0

constructor(heap: Array<T>, size){
    this.heap = heap as Array<T?>
    this.size = size
}

Но теперь он не принимает массивы с нулевыми значениями, когда Я пытаюсь создать экземпляр массива со значениями, допускающими обнуление

var arr = arrayOfNulls<String>(9)
var heap = Heap(arr, arr.size-1)

Так что теперь мне нужны два конструктора? Что происходит

Несоответствие типов. Требуется: Сравнимо Найдено: Строка?

Даже при Àrray<T?> есть ошибки с сравнению, чтобы не принимать значения NULL.

Даже с проверками все равно выдает ошибку

return  if(pos2 > size || (heap[pos1] == null || heap[pos2] == null)) false
        else heap[pos1].compareTo(heap[pos2]) < 0

Несоответствие типов. Обязательно: T Найдено: T?

Как разрешить массиву содержать нулевые значения?

Надеюсь, без использования Array<T> обнуляемого, поскольку это нарушает сравнение, и множественные конструкторы кажутся плохим дизайном.


Ответы [ 2 ]

1 голос
/ 05 марта 2020

С массивами сложно работать в обобщенном c классе, потому что они имеют усовершенствованные типы.

Я думаю, вам нужно переместить свойство резервного массива из конструктора, чтобы конструктор не был неоднозначным о обнуляемости. В противном случае вывод типа не будет работать с конструктором, и вы также будете вынуждены использовать массив с типом NULL в качестве параметра конструктора, даже если вы хотите, чтобы куча имела тип, не имеющий значения NULL. В любом случае, хорошо, что вы копируете массив из конструктора, а не используете его непосредственно в качестве резервного массива, потому что в противном случае есть вероятность, что какой-то внешний объект все еще может изменять этот резервный массив.

Поскольку массивы всегда имеют Реализованный тип, я думаю, вам также нужно использовать резервный массив типа Any?. Это будет означать, что вы должны приводить объекты, передаваемые из кучи, в функции publi c. Для этого вы можете создать одну функцию get и использовать ее для внутреннего использования с this[], чтобы избежать необходимости наведения повсюду в вашем классе. Непроверенное приведение безопасно, если вы не помещаете ничего, кроме T, в часть массива ниже size и не пытаетесь получить значения выше size, поскольку они всегда равны нулю и T может быть не обнуляемым.

class Heap <T : Comparable<T>>(initialValues: Array<T>, private var size: Int) {
    init {
        if (size > initialValues.size) 
            error("Initial array is too small for initial size.")
    }
    private var heap = Array<Any?>(size){ initialValues[it] }

    @Suppress("UNCHECKED_CAST")
    private operator fun get(index: Int) = heap[index] as T

    private fun increaseSize(){
        var enlargedHeap = arrayOfNulls<Any>(heap.size * 2)
        System.arraycopy(heap, 0, enlargedHeap, 0, size)
        heap = enlargedHeap
    }

    fun pop(): T {
        if (size == 0)
            error("Cannot pop when size is 0")
        size--
        val out = this[size] // Use this class's getter to avoid having to cast
        heap[size] = null
        return out
    }

    fun push(value: T) {
        if (size == heap.size)
            increaseSize()
        heap[size] = value
        size++
    }

}

Вы также можете рассмотреть возможность перемещения параметра size из конструктора, чтобы упростить использование и избежать неоднозначного случая того, что происходит, если входные данные size меньше чем массив, который вы передаете.

class Heap <T : Comparable<T>>(initialValues: Array<T>) {
    private var size = initialValues.size
    private var heap = Array<Any?>(size){ initialValues[it] }
    //...
1 голос
/ 05 марта 2020

Пожалуйста, используйте Array<T?> вместо Array<T>.

Например, в сигнатуре вашего класса свойства должны быть:

class Heap <T : Comparable<T>>(private var heap: Array<T?>, private var size: Int) {

Если вам нужен ввод с ненулевыми значениями, используйте что-то вроде этого:

     private var heap = initialHeap.toArray(); // copy input parameter to internal array

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...