Kotlin Collections - max, maxOf, maxBy, maxWith, maxOfWith

Murjune·2023년 1월 9일
2

kotlin/Java

목록 보기
2/5
post-thumbnail

프로그래밍 언어로 개발을 하다보면 해당 자료구조의 최댓값 혹은 최솟값을 구해야하는 경우가 필연적으로 생깁니다!! 따라서, 거의 모든 언어에서 해당 자료구조의 최댓값을 편하게 구할 수 있도록 내장함수를 제공해 주는데요

Kotlin Collections에서는 개발자의 편이를 위해 max(), maxOf, maxBy(),maxWith() 와 같은 최대값 함수를 제공해줍니다!!

이제 적재적소에 적절한 함수를 사용하기 위해 정리해봅시다 ㅎ ㅎ

1. max(), maxOrNull()

public fun <T : Comparable<T>> Iterable<T>.max(): T? {
    return maxOrNull()
}

public fun <T : Comparable<T>> Iterable<T>.maxOrNull(): T? {
    val iterator = iterator()
    if (!iterator.hasNext()) return null
    var max = iterator.next()
    while (iterator.hasNext()) {
        val e = iterator.next()
        if (max < e) max = e
    }
    return max
}

개발하면서 가장 많이 사용한 친구인 것 같다.

max()는 this가 Iterable한 객체이면서 제네릭 타입(T)이 Comparable하다면 자료구조 내 가장 큰 값을 갖는 T 객체를 반환한다.

val arr = listOf(1, 2, 3, 4) // 4
println(arr.max())

val arr2 = listOf("a", "b", "c") // "c"
println(arr2.max())

val arr3 = listOf<Int>()
println(arr3.max()) // java.util.NoSuchElementException
	

max() 함수는 Nullable하기 때문에 위의 예제처럼 emptyList에서 max()를 호출 시 Exception이 발생한다.

Kotlin 1.5 버전부터 Error를 발생시키고, 1.6 버전부터 완전히 deprecated된다고 한다!!

코틀린에서는 NoSuchElementException
이 발생할 위험이 있는 함수는 웬만하면 **OrNull() 함수를 지원하는데 max()도 마찬가지이다.(필자는 현재 타입 스크립트를 쓰고 있는데.. 진짜 코틀린은 GOD 인거 같다 🌠)

그래서, max()함수를 사용하지 말고 maxOrNull() 함수를 사용하도록 하자!!

val arr3 = listOf<Int>()
println(arr3.maxOrNull()) // null, 이제 Exception이 발생하지 않는다 ㅎ ㅎ

2. maxOf(), maxOfOrNull()

public inline fun <T, R : Comparable<R>> Iterable<T>.maxOf(selector: (T) -> R): R {
    valiterator= iterator()
    if (!iterator.hasNext()) throw NoSuchElementException()
    varmaxValue=selector(iterator.next())
    while (iterator.hasNext()) {
        valv=selector(iterator.next())
        if (maxValue<v) {
maxValue=v
}
    }
    return maxValue
}

public inline fun <T, R : Comparable<R>> Iterable<T>.maxOfOrNull(selector: (T) -> R): R? {
    val iterator = iterator()
    if (!iterator.hasNext()) return null
    var maxValue = selector(iterator.next())
    while (iterator.hasNext()) {
        val v = selector(iterator.next())
        if (maxValue < v) {
            maxValue = v
        }
    }
    return maxValue
}

maxOf() 함수는 2개 이상의 Element 들 중 가장 큰 값을 반환해주는 함수이다.

max()와 다른 점은 T가 Comparable하지 않아도 된다는 점이며, 대신 selector 람다식의 결과값인 R이 Comparable해야 하고 maxOf()의 결과값이 R이다.

kotlin 스코프 함수인 let, with, run 처럼 람다식의 return값이 해당 함수의 return값이다

말로 하는 것보다 예제를 직접 따라쳐보면서 느낌을 익히는 것이 더 이해하기 쉬울 것이다 😀 

val pArr = listOf(Pair("2", 3), Pair("2", 2), Pair("3", 43))

    println(pArr.maxOfOrNull { it.second }) // 43

maxOf() 또한 NoSuchElementException이 발생할 위험이 있으니 maxOfOrNull()을 사용하였다 ㅎ ㅎ

Pair는 Comparable하지 않으므로, maxOrNull() 함수를 사용할 수 없다.

따라서, 이러한 경우 maxOfOrNull()을 사용해주면 좋다.

selector의 return값인 it.second 가 maxOfOrNull() return값으로 나왔다

정리하면, Iterable의 T가 Comparable하지 않고, T객체들의 element들 중 최댓값인 element을 도출해내고 싶을 때 maxOfOrNull() 을 사용하면 될 것이다.

  • R들 중 max값이라는 의미에서 maxOf()이라는 네이밍을 갖는 것 같다.

3. maxBy(), maxByOrNull()

public inline fun <T, R : Comparable<R>> Iterable<T>.maxBy(selector: (T) -> R): T? {
    return maxByOrNull(selector)
}

public inline fun <T, R : Comparable<R>> Iterable<T>.maxByOrNull(selector: (T) -> R): T? {
    val iterator = iterator()
    if (!iterator.hasNext()) return null
    var maxElem = iterator.next()
    if (!iterator.hasNext()) return maxElem
    var maxValue = selector(maxElem)
    do {
        val e = iterator.next()
        val v = selector(e)
        if (maxValue < v) {
            maxElem = e
            maxValue = v
        }
    } while (iterator.hasNext())
    return maxElem
}

maxOf()와 거의 비슷하다!! maxOf()를 잘 이해했다면 maxBy()는 정말 쉽다.

maxBy()는 maxOf()와 동일한데, T객체들의 element들 중 최댓값에 해당하는 element가 속하는 T객체를 반환한다. 아래 예제를 보면 바로 이해가 갈 것이다 ㅎ ㅎ

fun main() {
    val pArr = listOf(Pair("2", 3), Pair("2", 2), Pair("3", 43))
    (1..2).max()
    println(pArr.maxOfOrNull { it.second }) // 43
    println(pArr.maxByOrNull { it.second }) // (3, 43)
}
  • R값에 의해 Iterable의 max값이 결정되어 maxBy()이라는 네이밍을 갖는 것 같다.

4. maxWith(), maxWithOrNull()

public fun <T> Iterable<T>.maxWith(comparator: Comparator<in T>): T? {
    return maxWithOrNull(comparator)
}

public fun <T> Iterable<T>.maxWithOrNull(comparator: Comparator<in T>): T? {
    val iterator = iterator()
    if (!iterator.hasNext()) return null
    var max = iterator.next()
    while (iterator.hasNext()) {
        val e = iterator.next()
        if (comparator.compare(max, e) < 0) max = e
    }
    return max
}

maxWith()은 maxBy()와 비슷하게 max에 해당하는 T객체를 반환한다.

대신, 블럭 내부에 T객체들 Comparator 객체를 넣어주면 된다.

즉, maxWith() Comparable하지 않은 T 객체들의 최댓값을 구할 때 사용하는 함수이다.

val pArr = listOf(Pair("2", 3), Pair("2", 2), Pair("3", 43))
    (1..2).max()
    println(pArr.maxOfOrNull { it.second }) // 43
    println(pArr.maxByOrNull { it.second }) // (3, 43)
    println(
				// Comparator는 생략가능!!
        pArr.maxWithOrNull( Comparator { t1, t2 -> t1.second - t2.second})
    ) // (3, 43)
  • 비교 기준인 Comparator가 필요해서 maxWith()이라는 네이밍을 갖는 것 같다.

5. maxOfWith(), maxOfWithOrNull()

public inline fun <T, R> Iterable<T>.maxOfWith(comparator: Comparator<in R>,selector: (T) -> R): R {
    valiterator= iterator()
    if (!iterator.hasNext()) throw NoSuchElementException()
    varmaxValue=selector(iterator.next())
    while (iterator.hasNext()) {
        valv=selector(iterator.next())
        if (comparator.compare(maxValue,v) < 0) {
maxValue=v
}
    }
    return maxValue
}

public inline fun <T, R> Iterable<T>.maxOfWithOrNull(comparator: Comparator<in R>, selector: (T) -> R): R? {
    val iterator = iterator()
    if (!iterator.hasNext()) return null
    var maxValue = selector(iterator.next())
    while (iterator.hasNext()) {
        val v = selector(iterator.next())
        if (comparator.compare(maxValue, v) < 0) {
            maxValue = v
        }
    }
    return maxValue
}

maxOfWith()는 maxOf()거의 동일한데 R값과 비교 기준을 바꾸고 싶을 때 사용하는 함수이다.

  • maxOf() 함수에 비교를 수행해줄 comparator를 추가로 입력받아 가장 큰 값을 반환 해준다.

잘 사용하진 않는 친구다.. 코테 풀 때 딱 한 번 사용해본 적 있다..

fun main() {
    val pArr = listOf(Pair("2", 3), Pair("2", 2), Pair("3", 43))

    println(pArr.maxOfOrNull { it.second }) // 43
    println(
        pArr.maxOfWithOrNull({ t1, t2 -> t1 - t2}) {
					it.second * 3 + it.first.toInt()}
    ) // 132
}

[참고] 최솟값

최솟값의 경우에는 위의 함수들의 max이름을 min으로 바꾸기만 하면 됩니다 ㅎ ㅎ

profile
열심히 하겠슴니다:D

0개의 댓글