티스토리 뷰

Kotlin

Kotlin에서 emptyList와 listOf

merlin.park 2020. 3. 9. 14:40

Kotlin을 개발하다 보면 listOf()와 emptyList()를 자주 사용하게 되는데, 두 함수의 내부가 궁금해졌다. 결론부터 적어보자면, 표현식이 다를 뿐 listOf()와 emptyList()는 Immutable List를 만들어내는 함수들이다. 아래는 이에 대해 알아본 내용이니 시간 있다면 한 번 보는 것도 나쁘지는 않다.

 

일단 emptyList와 listOf는 모두kotlin.collections 패키지 안에 포함되어있다. Kotlin에서 제공하는 Collection 지원들인데, 그렇다면 어떤 것이 다를까?

 

일단 emptyList의 코드는 아래와 같다.

/**
 * Returns an empty read-only list.  The returned list is serializable (JVM).
 * @sample samples.collections.Collections.Lists.emptyReadOnlyList
 */
public fun <T> emptyList(): List<T> = EmptyList

함수 emptyList의 반환값은 EmptyList라는 Object임을 알 수 있다. 그렇다면 EmptyList 오브젝트는 어떤 것일까?

internal object EmptyList : List<Nothing>, Serializable, RandomAccess {
    private const val serialVersionUID: Long = -7390468764508069838L
    
    override fun equals(other: Any?): Boolean = other is List<*> && other.isEmpty()
    override fun hashCode(): Int = 1
    override fun toString(): String = "[]"

    override val size: Int get() = 0
    override fun isEmpty(): Boolean = true
    override fun contains(element: Nothing): Boolean = false
    override fun containsAll(elements: Collection<Nothing>): Boolean = elements.isEmpty()

    override fun get(index: Int): Nothing = throw IndexOutOfBoundsException("Empty list doesn't contain element at index $index.")
    override fun indexOf(element: Nothing): Int = -1
    override fun lastIndexOf(element: Nothing): Int = -1

    override fun iterator(): Iterator<Nothing> = EmptyIterator
    override fun listIterator(): ListIterator<Nothing> = EmptyIterator
    override fun listIterator(index: Int): ListIterator<Nothing> {
        if (index != 0) throw IndexOutOfBoundsException("Index: $index")
        return EmptyIterator
    }

    override fun subList(fromIndex: Int, toIndex: Int): List<Nothing> {
        if (fromIndex == 0 && toIndex == 0) return this
        throw IndexOutOfBoundsException("fromIndex: $fromIndex, toIndex: $toIndex")
    }

    private fun readResolve(): Any = EmptyList
}

결국 내용은 "아무것도 없는 빈 List"와 동일하다. List 인터페이스를 구현하고 있으며, override된 함수들을 모두 보면 비어있는 리스트의 반환 값들을 정적으로 선언해 놓았다. 즉 "비어있는 Immutable List"라고 보면 된다.

 

그럼 다음으로 listOf()를 확인해보자.

/**
 * Returns an immutable list containing only the specified object [element].
 * The returned list is serializable.
 * @sample samples.collections.Collections.Lists.singletonReadOnlyList
 */
public fun <T> listOf(element: T): List<T> = java.util.Collections.singletonList(element)

/**
 * Returns a new read-only list of given elements.  The returned list is serializable (JVM).
 * @sample samples.collections.Collections.Lists.readOnlyList
 */
public fun <T> listOf(vararg elements: T): List<T> = if (elements.size > 0) elements.asList() else emptyList()

/**
 * Returns an empty read-only list.  The returned list is serializable (JVM).
 * @sample samples.collections.Collections.Lists.emptyReadOnlyList
 */
@kotlin.internal.InlineOnly
public inline fun <T> listOf(): List<T> = emptyList()

세 가지의 listOf 프로토타입이 있는데, 앞의 두 가지는 전달받은 값을 포함하는 ImmutableList라고 볼 수 있다. 그리고 아래 코드를 보면 아무런 인자도 전달받지 않은 listOf() 함수가 있는데 이 값이 emptyList()를 반환하는 것을 알 수 있다.

 

좀 더 명확하게 빈 리스트를 보여주기 위해서는 emptyList()를 쓰는 것이 코드리뷰와 같은 가독성에 있어서는 괜찮은 컨벤션이 되지 않을까 싶다. listOf()인데 인자가 없는 경우에 대해서는 리뷰 단계에서 누락과 같은 요인을 확인해볼 수도 있을 것 같다.

 

@inlineOnly가 있는 것으로 봐서 결국 listOf()로 작성된 코드는 오버헤드를 줄이기 위해 emptyList()로 치환될 것을 알 수 있다. inline에 대해서도 더 적어볼까 하였는데 내용이 꽤나 많아서 다른 글로 알아보는 것이 좋을 것 같다. 아마도 다음 글이 되지 않을까 싶다.

 

오랜만에 쓰는 글인데, 앞으로는 꼭 생소하거나 특별해보이는 이슈가 아니더라도 소소하게 이렇게 글을 남겨볼까 한다. 예전에는 검색했을 때 많이 나오는 내용이면 잘 안 쓰게 되었는데 이제는 있는 내용이더라도 찾아본 내용에 대해서는 내 방식대로 생각 정리해보는 게 어떨까 싶다. 그런 의미로 보시는 분이 보기에 글의 정성(?)은 좀 부족할 수 있으나 글을 쓰는 시간 동안의 생각정리에 좀 더 주안점을 두고, 기록 자체에 비중을 둘까 한다.

댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
글 보관함