모든 내용은 Do it! 코틀린 프로그래밍을 바탕으로 정리한 것입니다.
배열 다루기
코틀린에서 배열은 Array 클래스로 표현함
배열 사용 방법
[ 기본적인 배열 표현 ]
- arrayOf() : 기본 생성자
- Array() : 기본 생성자
- arrayOfNulls() : 빈 상태의 배열
val numbers = arrayOf(4, 5, 6, 3)
val animals = arrayOf("Cat", "Dog", "Lion")
[ 다차원 배열 ]
val array1 = arrayOf(1, 2, 3)
val array2 = arrayOf(4, 5, 6)
val array3 = arrayOf(7, 8, 9)
// 방법 1
val arr2d = arrayOf(array1, array2, array3)
// 방법 2
val arr2d = arrayOf(arrayOf(1, 2, 3), arrayOf(4, 5, 6), arrayOf(7, 8, 9))
2차원 이상의 배열도 표현 가능함.
하지만 너무 많은 차원의 배열은 접근하기 복잡해 버그가 발생활 확률이 높아짐!
[ 여러 가지 자료형이 혼합된 배열 ]
특정 자료형으로 제한하지 않는다면 배열의 요소로 정수, 문자열, Boolean 등 여러 가지 자료형을 혼합할 수 있음
특정 자료형으로 제한하기 위해서는
- arrayOf<자료형>()
- 자료형+ArrayOf()
- 기본형의 배열 : charArrayOf(), booleanArrayOf(), longArrayOf(), shortArrayOf(), byteArrayOf(), intArrayOf() 등
- 부호 없는 자료형에 대한 배열 : ubyteArrayOf(), ushortArrayOf(), uintArrayOf(), ulongArrayOf()
[ 배열 요소에 접근하기 ]
Array 클래스는 get()과 set() 메서드를 가지고 있는데 이것은 요소에 접근하기 위한 getter/setter임.
대괄호를 사용해도 접근할 수 있는데 이것은 연산자 오버로딩으로 정의되었기 때문
import java.util.Arrays
fun main() {
val arr = intArrayOf(1, 2, 3, 4, 5)
println("arr: ${Arrays.toString(arr)}")
println("size: ${arr.size}")
println("sum(): ${arr.sum()}")
// getter에 의한 접근
println(arr.get(2))
println(arr[2])
// setter에 의한 값 설정
arr.set(2, 7)
arr[0] = 8
println("size: ${arr.size} arr[0]: ${arr[0]}, arr[2]: ${arr[2]}")
// 루프를 통한 배열 요소 접근
for(i in 0..arr.size-1) {
println("arr[$i] = ${arr[i]}")
}
}
- Arrays.toString() : 배열의 내용을 문자열로 변환하여 출력 (java.util.Arrays를 임포트하여 문자열 변환을 쉽게 함)
- Arrays.deepToString() : 다차원 배열을 문자열로 변환하여 출력
- size : 배열의 크기
- sum() : 배열의 합을 계산
- 배열의 요소 접근 : arr.get()과 arr[]은 동일한 표현
- 배열의 요소 설정 : arr.set(인덱스, 값)과 arr[인덱스] = 값은 동일한 표현
- 루프를 통한 배열 접근 : in 키워드를 사용하며, 범위 지정자를 통해 0부터 size-1까지 반복(인덱스는 항상 0에서 시작하므로)
[ 표현식을 통해 배열 생성하기 ]
val | var 변수명 = Array(요소 개수, 초깃값)
- Array() 생성자의 첫번째 인자 : 요소 개수 - 표현식 결과에 대한 요소 개수
- Array() 생성자의 두번째 인자 : 초깃값 - 배열의 요소를 결정하는 표현식
- 람다식 초깃값 - init: (Int) -> T
- val arr3 = Array(5, {i -> i*2}) → [0, 2, 4, 6, 8]
배열 제한하고 처리하기
배열은 정의되면 배열의 길이와 내용이 메모리에 고정됨
→ 선언된 배열을 잘라내거나 덧붙일 수 없음
만일 고정되지 않은 동적 배열이 필요하다면 컬렉션의 하나인 List를 사용해야 함
[ 배열에 요소 추가하고 잘라내기 ]
배열이 정의되면 고정되기 때문에 새롭게 할당하는 방법으로 요소를 추가하거나 잘라낼 수 있음
val arr1 = intArrayOf(1, 2, 3, 4, 5)
val arr2 = arr1.plus(6)
println("arr2: ${Arrays.toString(arr2)}") // arr2: [1, 2, 3, 4, 5, 6]
val arr3 = arr1.sliceArray(0..2)
println("arr3: ${Arrays.toString(arr3)}") // arr3: [1, 2, 3]
- plus(새로운 요소) : 새로운 요소를 추가하기 위한 메서드
- sliceArray(범위의 시작 인덱스..끝 인덱스) : 배열을 자르기 위한 메서드
[ 기타 배열 관련 API ]
코틀린 표준 라이브러리 Array의 유용한 메서드
- arr.first() : 첫번째 요소 확인
- arr.last() : 마지막 요소 확인
- arr.indexOf(인덱스) : 요소의 인덱스 출력
- arr.average() : 배열의 평균 값 출력
- arr.count() : 요소 개수 세기
- reversedArray(), reverse() : 요소의 순서를 뒤집는 메서드
- arr.sum() : 요소를 합산하는 메서드
- arr.contains(요소) : 요소가 포함되어 있는지 확인하는 메서드 (요소 in arr 과 같은 표현)
[ Any로 선언된 배열 ]
자료형이 지정된 배열은 다른 자료형으로 변환할 수 없음
BUT, Any 자료형으로 만들어진 배열은 기존 자료형을 다른 자료형으로 지정할 수 있음
fun main() {
val b = Array<Any>(10, {0})
b[0] = "Hello World"
b[1] = 1.1
println(b[0])
println(b[1])
println(b[2])
}
0으로 채워진 배열 10개를 생성했고, 이후 문자열과 실수형 값을 지정하고 있음
처음에는 배열의 모든 요소가 정수형이었지만, 할당한 값에 따라 요소의 자료형이 변환되었음
→ Any를 사용하면 한 번에 기본적인 초기화를 하고 나중에 원하는 자료형으로 요소를 초기화할 수 있는 편리성이 있음
[ 멤버 메서드를 통한 배열 순환 ]
fun main() {
var arr = intArrayOf(1, 2, 3, 4, 5)
arr.forEach { element -> print("$element") }
arr.forEachIndexed { i, e -> println("arr[$i] = $e") }
val iter: Iterator<Int> = arr.iterator()
while (iter.hasNext()) {
val e = iter.next()
print("$e ")
}
}
- forEach() : 요소 개수 만큼 지정한 구문을 반복 실행
- forEachIndexed() : 순환하며 인덱스까지 출력
- 인덱스는 i로, 요소는 e로 받아 화살표 표현식 오른쪽 구문에 넘겨 인덱스와 함께 출력
- iterator() : 반복을 위한 요소를 처리하는 메서드
- hasNext() : 배열에서 참조할 다음 요소가 있는지 확인하는 메서드
- next () : 다음 요소를 반환하는 메서드
배열 정렬하기
정렬(sort)는 특정한 순서에 따라 나열한다는 것 (오름차순 - ascending, 내림차순 - descending)
어떤 정렬 알고리즘 기법을 사용하느냐에 따라 성능이 많이 좌우되며, 직접 정렬 기능을 만들어 쓸 수도 있지만
Array는 기본적인 정렬 알고리즘을 제공하고 있음
[ 기본 정렬 ]
fun main() {
val arr = arrayOf(8, 4, 3, 2, 5, 9, 1)
val sortedNum = arr.sortedArray()
println("ASC : "+Arrays.toString(sortedNum)) // ASC : [1, 2, 3, 4, 5, 8, 9]
val sortedNumDesc = arr.sortedArrayDescending()
println("DEC : "+Arrays.toString(sortedNumDesc)) // DEC : [9, 8, 5, 4, 3, 2, 1]
arr.sort(1, 3)
println("ORI : "+Arrays.toString(arr)) // ORI : [8, 3, 4, 2, 5, 9, 1]
arr.sortDescending()
println("ORI : "+Arrays.toString(arr)) // ORI : [9, 8, 5, 4, 3, 2, 1]
val listSorted: List<Int> = arr.sorted()
val listDesc: List<Int> = arr.sortedDescending()
println("LST : "+listSorted) // LST : [1, 2, 3, 4, 5, 8, 9]
println("LST : "+listDesc) // LST : [9, 8, 5, 4, 3, 2, 1]
val items = arrayOf<String>("Dog", "Cat", "Lion", "Kangaroo", "Po")
items.sortBy { item -> item.length }
println(Arrays.toString(items)) // [Po, Dog, Cat, Lion, Kangaroo]
}
- sortedArray(), sortedArrayDescending() : 정렬된 배열 반환
- sort(), sortDescending() : 원본 배열에 대한 정렬
- sort(fromIndex, toIndex), sortDescending(fromIndex, toIndex) : 특정 인덱스 구간만 정렬
- List 컬렉션으로 반환할 때도 해당 메서드를 사용하여 반환 (컬렉션은 Arrays.toString() 필요 없이 바로 출력 가능)
- sortBy() sortByDescending() : 특정 표현식을 넣어 정렬
[ 데이터 클래스 정렬 ]
데이터 클래스도 Array에서 확장된 sortBy() 함수를 이용하여 해당 멤버 변수에 따라 정렬할 수 있음
data class Product(val name: String, val price: Double)
fun main() {
val products = arrayOf(
Product("Snow Ball", 870.00),
Product("Smart Phone", 999.00),
Product("Drone", 240.00),
Product("Mouse", 333.35),
Product("Keyboard", 125.99),
Product("Monitor", 1500.99),
Product("Tablet", 512.99)
)
products.sortBy { it.price }
products.forEach { println(it) }
}
→ products를 it으로 넘기고 제품의 가격인 price가 제일 낮은 것을 기준으로 오름차순 정렬
- forEach나 sortBy처럼 소괄호를 사용하지 않고 있는 함수는 인자가 람다식 1개이므로 생략된 것
- Array<out T>는 값을 만들어내는 출력의 용도로 sortBy()는 Array의 확장 함수
- 람다식에 T 인자를 전달해 그 결과를 R?과 같이 null이 가능한 형식으로 받을 수 있음
- Ctrl+B 를 누르면 선언부를 참조해 함수의 정의를 확인할 수 있음
[ 비교자로 정렬 ]
public fun <T> Array<out T>.sortWith(comparator: Comparator<in T>) : Unit
fun main() {
val products = arrayOf(
Product("Snow Ball", 870.00),
Product("Smart Phone", 999.00),
Product("Drone", 240.00),
Product("Mouse", 333.35),
Product("Keyboard", 125.99),
Product("Monitor", 1500.99),
Product("Tablet", 512.99)
)
products.sortWith(
Comparator<Product> { p1, p2 ->
when {
p1.price > p2.price -> 1
p1.price == p2.price -> 0
else -> 1
}
}
)
products.forEach { println(it) }
}
- sortWith()는 Comparator를 매개변수로 가지고 있음을 알 수 있음
- Comparator는 자바의 인터페이스로서 2개의 객체를 비교하는 compare()를 구현함
- 람다식 p1, p2 -> when {...}을 이용해 비교 결과가 p1이 p2보다 크면 1, 같으면 0, 작으면 -1을 반환하도록 구현
products.sortWith(compareBy({it.name}, {it.price}))
products.forEach { println(it) }
- compareBy()를 함께 사용하면 이름을 먼저 정렬하고, 그 다음 이름이 동일한 경우 가격 기준으로 다시 정렬함
[ 배열 필터링 ]
filter() 메서드를 사용하면 원하는 데이터를 골라낼 수 있음
fun main() {
val fruits = arrayOf("Banana", "Avocado", "Apple", "Kiwi")
fruits.filter { it.startsWith("A") }
.sortedBy { it }
.map { it.toUpperCase() }
.forEach { println(it) } // APPLE AVOCADO
}
- filter()로 A로 시작하는 요소를 골라냄
- sortedBy()와 함께 필요한 정보를 골라내고 정렬함
- 골라낸 요소를 map으로 받아 toUppercase()로 대분자로 변경
- forEach()로 출력
이렇게 연속해서 호출하는 방법을 메서드 체이닝(Method Chaining)이라고 함
- 각 결과를 it으로 넘겨받아 처리할 수 있어 유용
- BUT 특정 메서드에서 오류가 나면 디버깅하기 어려워지기 때문에 주의해야 함
- 배열에 특정 요소가 있는지 확인하는 간단한 방법은 when문을 사용하는 것
println(products.minBy { it.price })
println(products.maxBy { it.price })
- 객체 product에서 가장 낮은 가격이나 높은 가격을 골라내기 위해서 minBy와 maxBy를 사용할 수 있음
배열 평탄화(flatten)
평탄화(flatten) : 다차원 배열을 단일 배열로 만드는 것 → flatten()
fun main() {
val numbers = arrayOf(1, 2, 3)
val strs = arrayOf("one", "two", "three")
val simpleArray = arrayOf(numbers, strs)
simpleArray.forEach { println(it) }
// [Ljava.lang.Integer;@677327b6
// [Ljava.lang.String;@14ae5a5
val flattenSimpleArray = simpleArray.flatten()
println(flattenSimpleArray) // [1, 2, 3, one, two, three]
}
- 첫번째 실행결과에서는 배열 자체가 들어있기 때문에 객체의 시그니처가 출력됨
- 평탄화 작업을 거치면 2개의 배열을 단일 배열로 만들 수 있음
* <out T>, <in T>에 대한 내용은 아래 포스팅에서 확인!
'Kotlin > Kotlin 프로그래밍' 카테고리의 다른 글
[Kotlin] 컬렉션(Collection)의 확장함수 (0) | 2021.05.27 |
---|---|
[Kotlin] 컬렉션(Collection) - Set, Map (0) | 2021.05.26 |
[Kotlin] 컬렉션(Collection) - List (0) | 2021.05.24 |
[Kotlin] 문자열(String) (0) | 2021.05.21 |
[Kotlin] 제네릭(Generic) (0) | 2021.05.18 |
[Kotlin] 다양한 클래스와 인터페이스 (3) (0) | 2021.05.13 |
[Kotlin] 다양한 클래스와 인터페이스 (2) (0) | 2021.05.10 |
[Kotlin] 다양한 클래스와 인터페이스 (1) (0) | 2021.04.21 |
댓글