En este tutorial examinarás la definición y uso de la función maxOrNull
en Kotlin para encontrar el elemento más valor grande de una colección. Se te proveerán ejemplos para ilustrar el propósito de la función y sus variaciones maxByOrNull()
, maxWithOrNull()
y maxOf()
.
Función maxOrNull()
La función maxOrNull()
en Kotlin te permite determinar el elemento con el valor máximo de una colección. Si la colección no tiene elementos, entonces retornará null
.
Ya que tenemos varios tipos de colecciones, existe una función de extensión asociada a cada uno como se muestra en las siguientes sintaxis:
// maxOrNull() en Arrays
fun Array<out Double>.maxOrNull(): Double?
fun Array<out Float>.maxOrNull(): Float?
fun <T : Comparable<T>> Array<out T>.maxOrNull(): T?
// maxOrNull() en Iterables
fun Iterable<Double>.maxOrNull(): Double?
fun Iterable<Float>.maxOrNull(): Float?
fun <T : Comparable<T>> Iterable<T>.maxOrNull(): T?
// maxOrNull() en Strings
fun CharSequence.maxOrNull(): Char?
Considera la cadena de texto «Develou». Si invocaras la función maxOrNull()
sobre ella ¿cuál sería el resultado?:
Evidentemente será el carácter 'v'
, ya que su código numérico es mayor que el resto de letras. La representación de este ejemplo en Kotlin es la siguiente:
fun main() {
val text = "Develou"
val max = text.maxOrNull()
println("Caracter con el código más grande: '$max'") // 'v'
}
Recuerda que el tipo de retorno es anulable, por lo que si la colección está vacía tendrás como resultado null
:
fun main() {
val emptyList = emptyList<Int>()
println(emptyList.maxOrNull()) // null
}
Función maxByOrNull()
La función maxByOrNull()
hace exactamente lo mismo que maxOrNull()
solo que toma como parámetro una función selectora. El objetivo de este parámetro es proveer el valor por el cual cada elemento será considerado mayor a otro. Además debe implementar a la interfaz Comparable
.
Por ejemplo, supón que dado tres puntos P1 (-1, 2), P2 (2, 1) y P3 (2, 2) en el plano coordenado XY, necesitas determinar cual tiene la mayor distancia hasta el origen O (0, 0).
import kotlin.math.hypot
fun main() {
val points = listOf(-1 to 2, 2 to 1, 2 to 2)
val farthestPoint = points.maxByOrNull(::calculateDistanceFromOrigin)
println("Punto más alejado: $farthestPoint")
}
private fun calculateDistanceFromOrigin(point: Pair<Int, Int>): Double {
val x = point.first.toDouble()
val y = point.second.toDouble()
return hypot(x, y)
}
Salida:
Punto más alejado: (2, 2)
Como ves, el punto más alejado se encuentra usando maxByOrNull()
con la referencia de la función calculateDistanceFromOrigin()
como selector. Esta recibe un par de enteros y en su interior hace una conversión a Double
para aplicar la función hypot()
, la cual calcula la raíz de la suma de cuadrados de ambas coordenadas sqrt(x2+y2)
.
Función maxWithOrNull()
Retorna el primero elemento con el valor más grande de acuerdo al parámetro Comparator
que hayas proveído.
Retomemos el mismo diseño de la clase para representar ríos y su largo en kilómetros que vimos en minOrNull()
:
data class River(val name: String, val length: Int)
Si quisiéramos encontrar el río más grande de una lista aplicamos a la función maxWithOrNull()
de la siguiente forma:
fun main() {
val rivers = listOf(
River("Nilo", 6690),
River("Mississippi", 6270),
River("Amazonas", 6387),
River("Negro", 2250)
)
val longestRiver = rivers.maxWithOrNull(compareBy { it.length })
println("El río más largo: $longestRiver")
}
Salida:
El río más largo: River(name=Nilo, length=6690)
Pasamos como instancia un comparador creado con la función compareBy()
, la cual recibe una lambda que selecciona a la propiedad River.length
como criterio comparable para los ríos. Como es esperado, el río Nilo será el resultado porque su tamaño es el más largo.
Función maxOf()
La función maxOf()
es similar a maxByOrNull()
, pero esta en vez de retornar null
cuando la colección es vacía, arroja una excepción del tipo NoSuchElementException
. Por ello su sintaxis no posee retorno anulable como vemos en la sintaxis para mapas:
inline fun <K, V, R : Comparable<R>> Map<out K, V>.maxOf(
selector: (Entry<K, V>) -> R
): R
Tomemos como ejemplo un mapa que almacena pares de parcelas de un terreno y su nivel de nitrógeno luego de aplicarle fertilizante. Se desea determinar cuál es el bloque con más concentración de nitrógeno, si no existe ningún dato entonces mostrar el mensaje de aviso:
fun main() {
val blocksAndNitrogen = mutableMapOf<String, Double>()
blocksAndNitrogen += "Bloque 1" to 15.843
blocksAndNitrogen += "Bloque 2" to 15.474
val maxNitrogenLevel = try {
blocksAndNitrogen.maxOf { it.value }
} catch (e: NoSuchElementException) {
"Ingresa al menos un dato para realizar el cálculo"
}
println("Nivel máximo de Nitrógeno: $maxNitrogenLevel")
}
Salida:
15.843
En el código anterior usamos una expresión try..catch
para asignar el valor a maxNitrogenLevel
. Si blocksAndNitrogen
no está vacío, se asignará el valor obtenido de maxOf()
. De lo contrario procesamos la excepción en el catch
y retornamos el String de aviso.
Si comentas las agregaciones de los dos elementos al mapa, obtendrás el siguiente resultado:
Ingresa al menos un dato para realizar el cálculo