Правильно возвращать Int из Hashmap в функцию - PullRequest
0 голосов
/ 09 января 2019

У меня есть функция в Kotlin, которая принимает аргумент Int и возвращает значение на основе формулы. Чтобы ускорить процесс, я сохраняю промежуточные результаты в HashMap<Int, Int>

private val calculatedResults = HashMap<Int, Int>()

private fun q2(n: Int): Int {
    if(calculatedResults.containsKey(n)) {
            return calculatedResults[n]
    }

    val q = ...
    calculatedResults[n] = q
    return q
}

Я получаю type mismatch of Int found but Int? required на

return calculatedResults[n]

Я не уверен, как правильно написать это. У меня было

return calculatedResults[n]!!

Но я не уверен, если это немного смешно.

Должен ли тип возвращаемой функции быть Int?, поскольку, хотя HashMap может содержать ключ n, значение может быть null? Разве это не значит, что HashMap должно быть <Int, Int?>?

Ответы [ 3 ]

0 голосов
/ 09 января 2019

Вы должны рассмотреть возможность переписать это в более идиоматический код. Это довольно простая задача для вычисления значения, если оно еще не установлено на карте:

fun q2(n: Int): Int {
    return calculatedResults.getOrPut(n) {
        42 //q
    }
}
0 голосов
/ 09 января 2019

getOrPut проверит, существует ли значение для n. Если значение существует, оно будет возвращено. Если значения не существуют, значение, возвращаемое лямбда-выражением, будет присвоено, а затем возвращено getOrPut.

Взгляните на этот пример:

fun calculate(n: Int) = n * 5 // some calculation

private fun q2(n: Int) = calculatedResults.getOrPut(n) {
    calculate(n)
}

Должен ли тип возвращаемой функции быть Int? потому что пока HashMap может содержать ключ n, значение может быть нулевым? Разве это не значит HashMap должен быть?

В этом случае ответ на вопрос очевидно «нет», потому что если значение отсутствует, просто добавьте его. Таким образом, значение, возвращаемое q2, никогда не может быть null.

0 голосов
/ 09 января 2019

Основная проблема заключается в том, что calculatedResults мог быть изменен другим потоком между содержащим (if(calculatedResults.containsKey(n))) и get (return calculatedResults[n]).

Даже после проверки с помощью containsKey calculatedResults[n] может, следовательно, вернуть неправильный результат, включая null, и такого способа проверки следует избегать.

Использование !! обычно следует использовать только в случае крайней необходимости, потому что это может вызвать NPE. Это определенно хаки.

Используйте следующий синтаксис для решения проблемы:

calculatedResults[n]?.let {
    return it
}

По сути, это хороший способ сказать

val result = calculatedResults[n]
if(result != null) {
    return result
}

см. https://kotlinlang.org/docs/reference/null-safety.html#safe-calls

Еще лучше использовать встроенный getOrPut :

private fun q2(n: Int): Int {
    return calculatedResults.getOrPut(n) { calcQ() }
}

private fun calcQ(): Int {
    val q = ...
    return q
}

Должен ли возвращаемый тип функции быть Int?, потому что, хотя HashMap может содержать ключ n, значение может быть нулевым? Разве это не значит, что HashMap должно быть <Int, Int?>?

Нет, вы правильно сделали, когда определили сигнатуру метода. Компилятор только жаловался, потому что то, что произошло в методе, не соответствовало этому. Если вы изменяете сигнатуру метода, вы просто перемещаете задачу в функцию, вызывающую q2, не обрабатывая ее.

...