제네릭과 배열 (3/3)

35990 단어 TIL_KotlinTILTIL

Do it! 코틀린 프로그래밍 [셋째마당, 코틀린 표준 라이브러리의 활용] 학습

✏️2. 배열 다루기✏️

배열(Array)

  • 동일한 자료형의 데이터를 연속적으로 나열한 형태
  • 순서 번호에 해당하는 인덱스(Index)와 값이 들어 있는 자료형에 따른 요소의 저장공간을 가짐
  • 코틀린에서는 요소에 여러 가지 자료형을 혼합해 구성할 수 있음

2-1. 배열을 사용하는 방법

📌 기본적인 배열 표현

  • 기본적인 배열을 생성하기 위해서는 arrayOf()Array() 생성자를 사용
  • 빈 상태의 배열을 지정하는 경우 arrayOfNulls()를 사용
val numbers = arrayOf(4, 5, 6, 7) // 정수형으로 초기화된 배열
val animals = arrayOf("Cat", "Dog", "Lion") // 문자열형으로 초기화된 배열

for (element in numbers) { } // 정숭형으로 초기화된 배열 출력

📌 다차원 배열

  • 기본적인 배열을 묶어서 2차원 이상의 배열로 표현하는 형태
val array1 = arrayOf(1, 2, 3)
val array2 = arrayOf(4, 5, 6)
val array3 = arrayOf(7, 8, 9)

val arr2d = arrayOf(array1, array2, array3)

// 간략화 한 선언 방법
val arr2d = arrayOf(arrayOf(1, 2, 3), arrayOf(4, 5, 6), arrayOf(7, 8, 9))

📌 배열에 여러 가지 자료형 혼합하기

  • 특정 자료형으로 제한하지 않는다면 배열의 요소로 정수, 문자열, 불린 등 여러가지 자료형을 혼합할 수 있음
    -> val mixArr = arrayOf(4, 5, 6, "Chike", false)

  • 특정 자료형으로 제한하려면 arrayOf<자료형 이름>() 형태나 자료형 이름 + ArrayOf() 형태의 조합으로 나타낼 수 있음
    -> val intOnlyArr1 = arrayOf<Int>(4, 5, 7, 3)
    -> val intOnlyArr2 = intArrayOf(4, 5, 7, 3)

  • 자료형 + ArrayOf() 형태 조합의 종류
    -> longArrayOf() shortArrayOf() byteArrayOf() 등..

📌 배열 요소에 접근하기

  • Array클래스는 게터/세터 를 가지고 있음
  • 대괄호를 사용해 접근할 수 있는데 이것은 연산자 오버로딩으로 정의되어 있기 때문
arr.get(index) -> value = arr[index]
arr.set(index) -> arr[index] = value

⬇️ 배열 요소에 접근 (get)

val arr = intArrayOf(1, 2, 3, 4, 5)
println(arr.get(2)) // 게터를 통한 접근
println(arr[2]) // 연산자 오버로딩으로 대괄호를 통한 접근

// 다차원 배열의 요소에 접근
println(arr2d[2][1])

⬇️ 배열 요소에 접근 (set)

arr.set(2, 7)
arr[0] = 8
arr2d[2][1] = 2

📌 표현식을 통해 배열 생성하기

  • val|var 변수 이름 = Array(요소 개수, 초깃값)
val arr3 = Array(5, {i -> i * 2})

-> arr3의 요소는 [0, 2, 4, 6, 8]


2-2. 배열 제한하고 처리하기

  • 배열은 정의되면 배열의 길이와 내용이 메모리에 고정
    -> 선언된 배열을 잘라내거나 덧붙일 수 없음
  • 고정되지 않은 동적인 배열이 필요하다면 List를 사용해야 함

📌 배열에 요소 추가하고 잘라내기

val arr1 = intArrayOf(1, 2, 3, 4, 5)

val arr2 = arr1.plus(6) // 하나의 요소를 추가한 새 배열 생성
val arr3 = arr1.sliceArray(0..2) // 필요한 범위를 잘라내 새 배열 생성

-> arr2는 [1, 2, 3, 4, 5, 6] , arr3는 [1, 2, 3]

📌 기타 배열 관련 API 사용하기

arr.first() // 첫 번째 요소 확인
arr.last() // 마지막 요소 확인
arr.indexOf(3) // 3번째 요소 확인
arr.average() // 배열의 평균 값 출력
arr.count // 요소의 개수 세기 (배열의 크기)
arr.reversedArray() // 요소의 순서를 뒤집기
arr.sum() // 요소를 합산
arr.contains() // 배열에 특정 요소가 포함되어 있는지 확인
// -> 지정한 요소가 포함되어 있다면 true를 반환

📌 Any로 선언된 배열

  • Any 자료형으로 만들어진 배열은 기존 자료형을 다른 자료형으로 지정할 수 있음
fun main() {
	val b = Array<Any>(10,{0})
    b[0] = "Hello World"
    b[1] = 1.1
}

-> 처음에는 배열의 모든 요소가 정수형이었으나 할당한 값에 따라 요소의 자료형이 변환

📌 멤버 메서드를 통한 배열 순환하기

  • 배열의 멤버 메서드인 forEach()forEachIndexed()를 사용해 요소 순환 가능
  • forEach() : 요소 개수만큼 지정한 구문을 반복 실행
  • forEachIndex() : 순환하며 인덱스까지 출력
arr.forEach { element -> print("$element ") }
// 인덱스는 i로 요소는 e로 받아 오른쪽으로 구문으로 넘김
arr.forEachIndexed({ i, e -> print("arr[$i] = $e") })
  • iterator() : 반복을 위한 요소를 처리하는 메서드
val iter: Iterator<Int> = arr.iterator()
while(iter.hasNext()) { // hasNext() : 다음 요소가 있는지 확인
	val e = iter.next() // next() : 다음 요소를 반환
}

2-3. 배열 정렬하기

📌 기본배열 정렬하고 반환하기

  • Array는 기본적인 정렬 알고리즘을 제공
  • sortedNums() sortedNumsDesc()
    -> 원본 배열에 정렬을 수행하지 않고 새로운 배열을 생성하여 할당
  • sort(fromIndex,toIndex) sortedNumsDesc()
    -> 원본 배열에 대한 정렬
    -> 인자를 지정하면 특정 구간만 배열 가능. 인자 미지정시 전체 요소를 대상으로 정렬
  • sorted() sortedDescending()
    -> 배열이 아닌 List로 반환
  • sortBy {}
    -> 특정 표현식을 넣어 정렬
fun main() {
	val arr = arrayOf(8, 5, 1, 9, 4)
    
    val sortedNums = arr.sortedArray()
    val sortedNumsDesc = arr.sortedArrayDescending()
    
    arr.sort(1, 3) 
    arr.sortDescending()
    
    val listSorted: List<Int> = arr.sorted()
    val listDesc: List<Int> = arr.sortedDescending()
    
    val items = arrayOf<String>("Dog", "Cat", "Lion", "Kangaroo", "Po")
    // 글자수가 짧은것부터 정렬
    items.sortBy { item -> item.length }
}

📌 sortBy()로 데이터 클래스 정렬하기

  • 멤버 변수에 따라 정렬 가능
data class Product(val name: String, val price: Double)

fun main() {
    val products = arrayOf(
            Product("one", 10.0),
            Product("two", 40.0),
            Product("three", 20.0)
    )
    
    // price가 제일 낮을 것을 기준으로 오름차순 정렬
    // 인자가 람다식 1개여서 소괄호 () 생략
    products.sortedBy { it.price }
}

📌 sortWith() 비교자로 정렬하기

  • Comparator는 자바의 인터페이스로서 2개의 객체를 구현하는 compare()를 구현
products.sortWith(
	//Comparator 객체를 이용해 o1이 o2보다 크면 1, 같으면 0, 작으면 -1
    Comparator<Product> { o1, o2 ->
		when {
        	o1.price > o2.price -> 1
            o1.price == o2.price -> 0
            else -> -1
        }
	}
)
// 이름 순으로 정렬 후 가격순으로 정렬
products.sortWith(compareBy({ it.name }, { it.price }))

📌 배열 필터링하기

  • filter() 메서드를 활용하여 원하는 데이터를 골라 낼 수 있음
fun main() {
    val fruits = arrayOf("banana", "apple", "avocado", "kiwi")
	// 메서드 체이닝
    fruits
            .filter { it.startsWith("a") }
            .sortedBy { it }
            .map { it.toUpperCase() }
            .forEach { println(it) }
}

-> filter() 메서드를 사용해 a로 시작하는 요소만 골라냄
-> sortedBy 로 배열을 오름차순으로 정렬
-> map으로 받아 대문자로 변경 후 출력

📍메서드 체이닝(Method Chaining)

  • 메서드를 연속해서 호출하는 방법
  • 코드를 간결하게 작성할 수 있음
  • 각 결과를 it으로 넘겨받아 처리할 수 있어 유용함
  • 특정 메서드에서 오류가 나면 디버깅하기 어려워짐
  • 초반 메서드에서 오류가 발생하면 후반 메서드는 작동하지 않아 '열차 사고'에 비유하기도 함

2-4. 배열 평탄화 하기

  • 배열 평탄화(flatten) : 다차원 배열을 단일 배열로 만드는 것
  • flatten() 메서드를 이용
fun main() {
    val numInt = arrayOf(1, 2, 3)
    val numString = arrayOf("one", "two", "three")
    
    // 2차원 배열
    val simpleArray = arrayOf(numInt, numString)
    
    // 단일 배열로 평탄화
    val flattenSimpleArray = simpleArray.flatten()
}

-> flattenSimpleArray는 [1, 2, 3, "one", "two", "three"] 형태의 단일 배열로 변환됨


✏️3. 문자열 다루기✏️

3-1. 문자열의 기본 처리

  • 문자열은 연속된 문자의 배열과 같음
  • 불변(immutable)값으로 생성되기 떄문에 참조되고 있는 메모리가 변경될 수 없음
  • 새로운 값을 할당하려고 한다면 기존 메모리 이외에 새로운 문자열을 위한 메모리를 만들어 할당해야함
    -> 기존 사용하던 메모리 공간은 GC(Garbage Collector)에 의해 제거
    -> GC에 의한 제거시점은 프로그래머가 제어할 수 없음 따라서 대량의 문자열을 다룰 떄는 메모리의 낭비가 일어나지 않도록 주의해야함
val hello: String = "Hello World!"

println(hello[0]) // H

hello[0] = 'K' // !오류!
hello = "bye" // 새로운 메모리 공간이 생성

📌 문자열 추출하고 병합하기

  • 문자열은 배열이기 때문에 인덱스로 특정 범위의 문자열을 추출할 수 있음
  • String.substring(인덱스 범위 지정): String
  • CharSequence.subSequence(인덱스 범위 지정): CharSequence
s = "abcdef"
s.substring(0..2) // 인덱스 0~2 범위인 abc 반환

// 문자열의 추출 및 병합
s = s.substring(0..1) + "x" + s.subString(3..s.length-1) // abxdef반환

-> s에는 새로운 문자열이 할당되며 "abcedf"는 어딘가 남아있다가 GC에 의해 제거됨

📌 문자열 비교하기

  • compareTo() 메서드를 사용하여 문자열을 비교 할 수 있음
var s1 = "Hello Kotlin"
var s2 = "Hello KOTLIN"

println(s1.compareTo(s2)) // false 반환
println(s1.compareTo(s2, true)) // 대소문자 무시, true 반환

📌 StringBuilder 사용하기

  • StringBuilder를 사용하면 문자열이 사용할 공간을 좀 더 크게 잡을 수 있음
  • 기존의 문자열보다는 처리속도가 느림
  • 단어를 변경하지 않고 그대로 사용하면 임시 공간인 메모리가 낭비됨
  • 문자열이 자주 변경되는 경우에 사용하는것이 좋음
var s = StringBuilder("Hello")
s[2]='x' // 문자열에선 허용되지 않았던 요소의 변경이 가능! Hexlo

-> s는 메모리 공간을 새롭게 만들어지지 않고 여유분의 공간을 이용해 문자 요소가 변경

  • StringBuilder의 기타 관련 메서드를 사용하면 포함(append), 추가(insert), 삭제(delete)가 용이해짐
  • 문자열을 포함시키기 위해 사용하는 append()는 생성된 버퍼를 사용하므로 보통 + 연산자를 이용해 새로운 객체를 만들어 처리하는것보다 더 좋음
s.append("World") // HexloWorld
s.insert(10, "Added") // HexloWorldAdded
s.delete(5, 10) // HexloAdded

좋은 웹페이지 즐겨찾기