코틀린 List와 Array

Lee Jun Hyeong·2022년 12월 26일
0

Kotlin 톺아보기 🙂

목록 보기
4/4

동적 자료구조, List

내부 구현

내부적으로 mutableListList 인터페이스에 캐스팅해서 사용한다.

public interface List<out E> : Collection<E> {
    // Query Operations

    override val size: Int
    override fun isEmpty(): Boolean
		.
		.
		.
    public fun indexOf(element: @UnsafeVariance E): Int
    public fun lastIndexOf(element: @UnsafeVariance E): Int
    public fun listIterator(): ListIterator<E>
    public fun listIterator(index: Int): ListIterator<E>
    public fun subList(fromIndex: Int, toIndex: Int): List<E>
}

List의 특징

  • 순서가 있는 자료형들의 집합 (시퀀스)
  • 불연속적인 메모리 공간을 점유하여 메모리 관리에 용이하다. → 포인터로 값을 접근
  • List 에서 인덱스는 몇 번째 데이터 인지를 의미한다.
  • listOf, mutableListOf() 등으로 리스트를 생성할 수 있다.
  • ListNull을 포함할 수 있고, 중복 요소를 허용한다.
  • 두 리스트가 서로 원소들의 인덱스, 내용이 모두 같으면 같은 리스트로 간주한다.
data class Person(
    val name: String,
    var age: Int,
)

fun main() {
    val jun = Person("Jun", 26)
    val number = listOf(Person("You", 26), jun, jun)
    val number2 = listOf(Person("You", 26), Person("Jun", 26), jun)
    println(number == number2)  // true
    jun.age = 27
    println(number == number2)  // false
}
  • Immutable List → 내용을 바꿀 수 없는 리스트
val name = listOf("jun", "lee", "kim")
println(name)
// [jun, lee, kim]
  • Mutable List → 내용을 자유자재로 바꿀 수 있는 리스트
    val name = mutableListOf("jun", "lee", "kim")
    println(name.remove("jun"))		// 1) true
    
    val numbers = mutableListOf(1, 2, 3, 4)
    numbers.add(5)			// 원소 추가
    numbers.removeAt(1)		// 1번 인덱스 값 삭제
    numbers[0] = 0			// 0번 인덱스에 값 0 변경
    numbers.shuffle()		// 배열 섞기
    println(numbers)
  • 1) remove() 메소드가 원소가 있으면 해당 원소를 삭제하고 성공 여부를 리턴한다.

List의 장점

  • 포인터를 사용하여 다음 값을 가르키고 있기 때문에 삽입과 삭제 동작이 빠르다.
  • 동적 타입이기 때문에 크기가 정해져있지 않아 자유롭게 사용할 수 있다.
  • 메모리 재사용이 용이하다.

List의 단점

  • 불연속적 메모리 공간을 사용하는 만큼, 검색 성능은 좋지 않다.
  • 포인터를 통해 다음 값을 가르키므로 포인터를 담기 위한 추가적인 메모리 공간이 필요하다.

정적 데이터 타입, Array

내부 구현

class Array<T> private constructor() {
    val size: Int
    operator fun get(index: Int): T
    operator fun set(index: Int, value: T): Unit

    operator fun iterator(): Iterator<T>
    // ...
}

Array의 특징

  • Array 는 생성한 순간, 사이즈는 고정되며 원소 삭제나 추가 등을 사용할 수 없다.
  • 대신 그 안에서의 원소 값 변경은 자유롭게 할 수 있다.
  • 코틀린에서는 따로 mutableArray 같은 건 없고 기본적으로 Mutable 동작한다.
  • 따라서 val 으로 선언하나 var 으로 선언하나 똑같은 동작을 한다.
  • arrayOf 키워드를 통해 선언할 수 있다. (출력 시 java.util.Arrays.toString() 또는 contentToString() 사용)
  • Array Utility 를 사용하지 않으면
    코틀린은 해당 Array 의 주소만을 출력한다.
    val name = arrayOf("jun", "lee", "kim")
    println(name)
    // [Ljava.lang.String;@17c68925
    
    println(name.contentToString())			
    println(java.util.Arrays.toString(name))
    // [jun, lee, kim]
  • Array 생성 시 간단한 초기화 작업도 가능하다. (자동으로 생성된 it키워드는 Array의 Index역할을 한다.)
// [0, 2, 4, 6, 8] 형태의 Array<Int> 생성
val array = Array (5) { it * 2 }
println(java.util.Arrays.toString(array))

// ["0", "1", "4", "9", "16"] 형태의 Array<String> 생성
val asc = Array(5) { i -> (i * i).toString() }
asc.forEach { println(it) }
  • 만약 Primitive 타입만 담는다면, 아래와 같이 선언해야한다.
val numbers = intArrayOf(1,2,3)

// Array of int [0, 0, 0, 0, 0]
val arr = IntArray(5)

// [42, 42, 42, 42, 42]
val arr = IntArray(5) { 42 }

// [0, 1, 2, 3, 4]
var arr = IntArray(5) { it * 1 }

이런식으로 Primitive Type Array 를 사용하면 Boxing Overhead 를 피할 수 있다.
Array 클래스와 상속관계는 없으나 메소드나 프로퍼티는 동일하다.
Array<Int>는 자바의 Integer[]와 매핑되고,
IntArray는 자바의 int[]에 매핑되기 때문에 Overhead 방지를 위해 꼭 구분지어 사용해야한다.

  • Boxing, 정수의 기본 타입에는 값을 직접 저장하는 int(primitive type)과 Integer(reference type)이 존재한다. 박싱(boxing)이란 기본형을 참조형으로 변환하는 것을 말한다.
  • Overhead, 어떤 처리를 하기 위해 들어가는 간접적인 처리 시간 · 메모리 등을 말한다.

장점

  • 인덱스를 통한 검색이 용이하다.
  • 연속된 메모리 공간을 사용하여 메모리 관리가 편리하다.

단점

  • 배열에서 값을 삭제해도 크기가 줄어들지 않기 때문에 메모리가 낭비된다고 볼 수 있다.
  • 배열은 정적 데이터 타입이므로 배열 크기를 컴파일 이전에 지정해줘야 하는 번거로움이 있다.
  • 배열의 크기를 이후에 재조정할 수 없기 때문에 사용하기 까다롭다.

List와 Array 공통점

  • 서로 다른 타입이 공존할 수 있다.
val mix = arrayOf("fish", 2)
  • 서로 합치기 가능하다.
val numbers = intArrayOf(1, 2, 3)
val numbers3 = intArrayOf(4, 5, 6)
val foo2 = numbers3 + numbers
println(foo2[5])  // 3
  • ArrayList 를 합친 새로운 중첩 List 를 생성하는 것도 가능하다.
val numbers = intArrayOf(1, 2, 3)
val oceans = listOf("Atlantic", "Pacific")
val oddList = listOf(numbers, oceans, "salmon")
println(oddList)

// [[I@89178b4, [Atlantic, Pacific], salmon]

List 와 Array 차이점

  • Array무조건 고정된 사이즈를 가짐 (고정된 연속 메모리 내에서 원소를 저장)
    반면 List사이즈를 변형할 수 있음 (불연속적인 메모리 공간을 가져 메모리 관리에 용이)
  • IntArray, ByteArray 등 Primitive 타입에 매핑되는 자료형을 제공하는 Array 와 다르게 일반적인 경우 ListPrimitive 타입에 최적화된 (매핑되는) 구현이 존재하지 않음 (JDK 외부 라이브러리 중 이를 제공하는 라이브러리가 있긴 함)
  • List 와 다르게 Array동등성 비교를 지원하지 않음
    즉, Array 의 경우 구성 원소의 인덱스와 값이 모두 같아도 false 를 리턴한다.
  • JVM 차원에서 봤을 때 Arrayarrays 를 말하고, Listjava.util.List 를 말한다.

참고
https://velog.io/@haero_kim/List-%EB%9E%91-Array-..-%EB%8C%80%EC%B2%B4-%EB%AC%B4%EC%8A%A8-%EC%B0%A8%EC%9D%B4%EC%95%BC
https://velog.io/@yangsijun528/%EC%98%A4%EB%B2%84%ED%97%A4%EB%93%9C%EB%9E%80
https://icat2048.tistory.com/466
profile
"왜" 사용하며, "어떻게" 사용하는지에 대해

0개의 댓글