본문 바로가기
  • 개발하는 곰돌이
Programming Language/Kotlin & Java

Kotlin 기본 문법 5 : 배열

by 개발하는 곰돌이 2022. 11. 25.

목차

    개요

    전통적인 프로그래밍 언어에서의 배열은 같은 타입의 변수들을 원소로 갖는 유한집합으로 정의된다. Kotlin의 경우에는 기본적인 개념은 비슷하지만 약간의 차이가 있는데 모든 자료형이 참조 타입, 즉 클래스이기 때문에 자료형의 종류에 구애받지 않고 모든 자료형을 담을 수 있는 배열도 정의가 가능하다는 점이다.

    이번 포스트에서는 Kotlin의 배열에 대해 정리하고자 한다.

    Kotlin에서의 배열 선언

    Kotlin은 전통적인 프로그래밍 언어들이 일반적으로 사용하는 자료형이나 식별자 뒤에 대괄호를 붙이는 방식으로 배열을 선언할 수 없다. Kotlin에서는 전통적인 방식이 아닌 다른 방법으로 배열을 선언해야 하는데 이 방법으로는 크게 5가지가 있다.

    • arrayOf()
    • 자료형ArrayOf()    ex)intArrayOf()
    • Array()
    • 자료형Array()     ex)IntArray()
    • arrayOfNulls()

    각 방법은 서로 선언하는 방법과 구조 등에 차이가 있다.

    1. arrayOf(), 자료형ArrayOf()

    arrayOf()자료형ArrayOf()은 함수의 파라미터로 배열의 원소를 전달받아 배열을 생성한다. 두 방식은 배열이 담을 수 있는 자료형의 종류에 차이가 있다. arrayOf()를 사용할 경우에는 파라미터로 받은 값들을 토대로 자동으로 자료형을 추론한다. 또한, 처음 배열을 초기화할 때 여러 종류의 자료형을 섞어서 초기화할 수도 있다. 하지만 자료형ArrayOf()를 사용할 경우에는 해당 자료형만 원소로 가질 수 있으며 Java의 원시타입에 해당하는 자료형만 사용할 수 있다. 아래의 예시 코드를 보자.

    val arr1 = arrayOf(1, "2", 3L)		// OK
    val arr2 = intArrayOf(1, "2", 3L)	// 컴파일 에러

    arr1을 보면 배열을 생성하는 메소드에 자료형이 선언되지 않았기 때문에 Int, String, Long형이 혼재하는 배열이 만들어진다. 하지만 arr2의 경우엔 처음부터 intArrayOf()로 선언을 했기 때문에 Int형이 아닌 자료형을 파라미터로 전달하면 컴파일 에러가 발생하게 된다.

    또 다른 차이점으로는 arrayOf()는 파라미터를 전달하지 않으면 컴파일 에러가 발생하지만 자료형ArrayOf()는 파라미터를 전달하지 않아도 정상적으로 실행된다. 물론 이 경우에는 크기가 0인 배열이 생성되기 때문에 의미는 없다고 볼 수 있다.

    2. Array(), 자료형Array()

    Array()자료형Array()는 파라미터로 배열의 크기를 전달받아 배열을 생성한다. Array()의 경우에는 그 뒤에 중괄호로 배열 원소들의 초기값을 추가로 입력해야 한다는 차이가 있다. 만약 중괄호 내부에 아무것도 입력하지 않는다면 Unit 배열이 생성된다. 자료형Array()의 경우에는 중괄호로 배열 원소들의 초기값을 입력하는 것은 선택 사항이며 입력하지 않을 경우 숫자형은 0, 문자형은 공백문자, 논리형은 false로 초기화된다. 또한 자료형ArrayOf()와 마찬가지로 Java의 원시타입에 해당하는 자료형만 사용할 수 있다.

    1번 방법과 2번 방법은 작동하는 방식이 조금 다르다. 2번 방법은 배열 클래스의 생성자로 배열을 생성하는 것이지만 1번 방법은 메소드를 통해 2번의 생성자를 호출하여 배열을 생성한다.

    val arr1 = Array(3) { 1 }	// [1, 1, 1]
    val arr2 = IntArray(3)		// [0, 0, 0]
    val arr3 = BooleanArray(3) { true }	// [true, true, true]
    val arr4 = Array(5) {}	// Unit 배열
    val arr5 = Array(4)		// 컴파일 에러

    Array()Array<Any?>()와 같은 방식으로 선언하게 되면 null을 포함한 모든 타입을 원소로 가질 수 있는 배열을 생성할 수 있다. 단, 이 경우에도 중괄호로 배열 원소들의 초기값을 입력해야 한다. Any와 Any? 이외의 원소들은 arrayOf()와 마찬가지로 초기값을 토대로 자료형을 추론하여 배열을 생성한다.

    Any

    Kotlin의 Any는 Java의 Object 클래스처럼 Kotlin의 최상위 클래스이다.

    이 방법으로 배열을 생성할 때 초기값은 고정된 값 뿐만 아니라 각 인덱스를 기준으로 연산을 한 값을 초기값으로 하는 배열을 생성할 수도 있다. 아래의 예시에서 it은 각 인덱스값을 의미한다.

    val arr1 = Array(5) { "배열입니다".substring(it) }	// ["배열입니다", "열입니다", "입니다", "니다", "다"]
    val arr2 = IntArray(4) { it + 5 }	// [5, 6, 7, 8]
    val arr3 = IntArray(5) { it * it }	// [0, 1, 4, 9, 16]

    3. arrayOfNulls()

    arrayOfNulls() 메소드는 자료형을 지정하고 배열의 크기를 파라미터로 받아 null로 채워진 배열을 생성한다. 이 방법을 사용하면 배열의 초기값을 null 외의 다른 값으로 설정할 수 없다.

    val arr = arrayOfNulls<Int>(5)	// [null, null, null, null, null]
    arr[3] = 5	// arr = [null, null, null, 5, null]

    4. arrayOf(), Array()와 자료형arrayOf(), 자료형Array()의 차이

    이 두 경우는 실제로 생성되는 배열의 타입에 약간 차이가 있다. 아래는 각 방법으로 생성한 배열의 타입을 출력한 경우이다.

    각 방법으로 생성한 배열의 타입을 출력한 예시.

    위 사진을 보면 arrayOf()Array()로 생성한 배열은 Java의 Wrapper Class 배열이지만 intArrayOf()IntArray()로 생성한 배열은 Java의 원시타입 배열이라는 것을 알 수 있다. 즉, 자료형ArrayOf()자료형Array()의 자료형으로 Java의 원시타입만 사용할 수 있는 이유는 이 방법으로 생성한 배열이 내부적으로 Java의 원시타입 배열을 생성하기 때문이다.

    Kotlin에서의 2차원 배열 선언

    Kotlin은 2차원 배열을 선언하는 과정이 조금 복잡하다. Java라면 단순히 자료형 뒤에 대괄호 쌍을 2개 작성하는 것으로 손쉽게 2차원 배열을 선언할 수 있으나, Kotlin에서는 위의 배열 선언 메소드나 생성자로 설정하는 초기값에 다시 배열 선언을 하는 것으로 2차원 배열을 선언해야 한다.

    Kotlin에서 \(n\times m\) 크기의 배열을 선언하는 과정은 다음과 같다. 먼저 2차원 배열의 Array(n)으로 크기가 \(n\)인 배열을 선언한다. 이후 중괄호 내부의 초기값에 Array(m) 또는 자료형Array(m)를 선언하면 크기가 \(m\)인 배열을 \(n\)개 가진 2차원 배열, 즉 \(n\times m\) 크기의 2차원 배열이 만들어진다.

    val arr1 = Array(3) { IntArray(4) }
    /*
    [[0, 0, 0, 0],
    [0, 0, 0, 0],
    [0, 0, 0, 0]]
    */

    이 배열은 Java로 표현하면 아래와 같다.

    int[][] arr = new int[3][4];

    배열의 원소 가져오기

    배열의 원소에 접근하는 것은 다른 언어와 마찬가지로 배열의 이름에 대괄호를 이용하여 접근할 수 있다. Kotlin도 다른 언어들과 마찬가지로 배열의 인덱스는 0부터 시작한다.

    val arr = Array(5) { it * it }
    arr[2] = -10
    println(arr[2])		// -10
    println(arr[3])		// 9

    1차원 배열의 모든 원소를 문자열로 출력

    1차원 배열의 모든 원소를 문자열로 나타내고 싶을 때는 contentToString() 메소드와 joinToString() 메소드를 사용할 수 있다.

    • contentToString() : 배열을 대괄호로 감싸고 요소를 콤마로 구분한 문자열을 반환
    • jointToString() : 파라미터로 받은 문자열로 배열의 요소를 구분한 문자열을 반환(기본값 : ", ")
    val arr = arrayOf(1, 3, 5, 7, 9)
    println(arr.contentToString())	// [1, 3, 5, 7, 9]
    println(arr.joinToString())	// 1, 3, 5, 7, 9
    println(arr.joinToString(" / "))	// 1 / 3 / 5 / 7 / 9

    2차원 배열의 모든 원소를 문자열로 출력

    2차원 배열의 모든 원소를 문자열로 나타내고 싶을 땐 contentDeepToString() 메소드를 사용할 수 있다. 이 메소드를 사용하면 1차원 배열에서 contentToString() 메소드를 사용했을 때 처럼 배열의 내용이 출력된다.

    2차원 배열에서도 joinToString() 메소드를 사용할 수는 있지만 1차원 배열을 출력할 때와는 다르게 중괄호를 추가로 작성하여 중괄호 내부에 it.contentToString() 또는 it.joinToString()을 작성해야 2차원 배열의 모든 원소를 문자열로 나타낼 수 있다. 이 때, 외부의 jointToString() 메소드는 구분자로 기본값을 사용한다면 괄호를 생략할 수 있다.

    val arr = Array(3) { i -> IntArray(3) { it + i * 3 } }
    println(arr.contentDeepToString())
    println(arr.joinToString { it.contentToString() })
    println(arr.joinToString("\n") { it.joinToString(" - ") })
    
    /*
    [[0, 1, 2], [3, 4, 5], [6, 7, 8]]
    [0, 1, 2], [3, 4, 5], [6, 7, 8]
    0 - 1 - 2
    3 - 4 - 5
    6 - 7 - 8
    */
    위 예시 코드에서 배열 선언문의 i는 arr의 행을 의미한다. 이에 대한 자세한 내용은 추후 람다 함수에 대해 다룰 때 함께 이야기 할 것이다.

    마무리

    이번 포스트에서는 Kotlin의 배열에 대한 기본에 대해 정리해보았다. Kotlin의 배열에는 이외에도 여러 메소드들을 제공하는데 이에 대해서는 추후 따로 정리를 할 예정이다.

     

    다음 포스트에서는 Kotlin의 클래스와 접근 지정자에 대해 정리하고자 한다.

     

    댓글 피드백은 언제나 환영합니다.

     

    댓글