코틀린으로 코딩테스트 준비하기
최근에 대기업 코딩테스트를 돌아보던 중 안드로이드는 거의 다 자바도 아닌 코틀린만 응시가 가능한 기업들이 많았다. 평소에 개발로는 거의 사용하지 않는 파이썬으로 알고리즘을 풀다가, "이번 기회에 자주 사용하는 코틀린으로 코딩테스트를 보면 좋을 것 같다."라는 생각이 들어 시작하게 되었다.
필자의 게시글 중 해당 게시글만 인기가 많아, 게시글을 리마스터하였다. Update : (2023.08.03, 1.0.1v)
필자는 코틀린으로 문제를 약 300문제 가까이 풀었는데, 자주 사용하는 문법과 자료구조에 대해 설명한다.
코틀린 사용자 입력 받아 출력하기
기본 입출력 함수 : readLine(), print()
fun main(args: Array<String>) {
var name = readLine()
print("name $name") // 일반 출력
println("name = $name") // 출력 + 개행문자
}
코틀린의 println() 함수는 자바의 System.out.println() 함수와 동일하다. 해당 readLine(), println()은 사용이 간단한 대신 바이트 단위로 처리하고, 내부에서 정규식과 같은 검사를 진행하기 때문에 실행 속도가 상대적으로 느리다.
버퍼 입출력 함수 BufferedReader, BufferedWriter
import java.io.BufferedReader
import java.io.BufferedWriter
import java.io.InputStreamReader
import java.io.OutputStreamWriter
import java.util.StringTokenizer
fun main() {
// BufferedReader
val br = BufferedReader(InputStreamReader(System.`in`)) // BufferedReader 생성
val hello = br.readline() // BufferedReader로 사용자 입력 받기
// BufferedWriter
val bw = BufferedWriter(OutputStreamWriter(System.`out`)) // BufferedWriter 생성
bw.write("hello") // 일반 출력
bw.write(hello + "\n") // 출력 + 줄바꿈
bw.flush() // 남아있는 출력 비우기
bw.close() // 스트림 종료
}
BufferedReader 와 readLine()을 함께 사용함으로써 한 줄 전체를(공백 포함) 읽어, char 배열을 하나하나 생성할 필요 없이 String 타입으로 리턴한다는 장점이 있으며, 하나하나 문자를 보내는 것이 아닌 한 번에 Buffer(버퍼)에 모아둔 다음 출력하며, 정규식을 검사하지 않아 속도도 더 빠르다.
그럼 뭘 써야할까?
보통 입력식은 까다롭고 출력식은 용이하게 주어지는 경우가 많다. 따라서 속도가 빠른 BufferedReader와 사용하기 용이한 print()를 함께 사용하는 것을 추천한다.
Kotlin String
코틀린의 문자열은 코틀린의 장점중 하나이다. 문자열의 멤버와 확장 함수를 통해 손쉽게 사용할 수 있다.
fun main() {
val jainoString = "안녕하세요 정자이노입니다."
jainoString.length // 문자열의 길이 반환 > 14
jainoString.indices // 구성된 문자의 범위 반환 > 0 .. 13
jainoString.substring(startIndex = 1, endIndex = 3) // startIndex 와 endIndex 사이의 String 반환
jainoString.slice(1 .. 3) // 입력한 범위의 String 반환
jainoString.find{ char -> char == '안' } // 조건 식에 맞는 첫번째 문자 반환
jainoString.filter{ char -> char == '안' } // 조건 식에 맞는 모든 문자를 합친 문자열 반환
jainoString.take(3) // 앞의 문자 3개를 가진 문자열 생성 > 안녕하
jainoString.drop(3) // 앞의 문자 3개를 버린 문자열 생성 > 세요 정자이노입니다.
jainoString.replace(oldChar = '정', newChar = '김') // 문자 대치
jainoString.replace("a[bc]+d?".toRegex(), "") // 정규식 적용
}
작성한 코드를 살펴보면,
- length, indices -> 문자열의 길이와 문자열의 범위를 반환한다.
- subString, slice -> 문자열 슬라이싱 함수로, 인자로 인덱스의 범위나 첫 인덱스와 마지막 인덱스를 전달하여 슬라이싱된 문자열을 반환한다.
- find, filter -> find는 입력한 조건 식에 맞는 첫번째 문자를 반환하고, filter는 입력한 조건식에 맞는 문자를 모아 문자열을 반환한다.
- take, drop -> 입력한 n을 기준으로, take는 처음부터 n번째 문자열을 만들어 반환한다. drop는 처음부터 n번째 문자를 버린 나머지를 반환한다.
- replace -> 두개의 문자를 입력받아, 서로를 대치한다. 또한 정규식을 입력받아 수행할 수 있다.
이외에도 문자열을 정렬하거나, 변환하는 함수가 있다. 더 알아보고 싶으면 공식문서를 확인하자.
Kotlin Array
배열은 여러개의 값을 저장하기 위해 각 배열의 인덱스가 연속된 메모리 공간을 차지하는 정적(Static)인 자료구조이다.
배열을 생성할 때 크기와 자료형을 설정하고 이후에 크기나 자료형을 변경할 수 없다. 배열의 인덱스를 통해 시간 복잡도 O(1)으로 접근이 가능하다.
원시타입 배열
코틀린에서 배열은 두가지 원시타입과 참조타입으로 사용할 수 있다. 원시타입이 참조타입에 비해 메모리 사용량, 접근 속도, 확장함수 측면에서 뛰어나다. 기본 자료형의 경우는 원시타입을 사용하고, class나 interface의 배열을 만들 때만 참조 타입을 사용하는 것을 추천한다. 아래의 배열 예시는 모두 원시타입에 대한 예제와 소개를 할 예정이다.
배열 사용법
배열은 Array앞에 자료형을 붙여서 사용할 수 있다. 배열은 선언하는 방법은 두 가지가 있다. 선언 시 크기를 사용하는 경우와 지정한 값을 사용하는 경우로 나눌 수 있다. 지정한 값을 사용할 때에는 Array 뒤에 Of를 붙여서 사용할 수 있다.
fun main() {
// 원시타입은 Array 앞에 자료형을 붙여 사용할 수 있다. ex) BooleanArray, StringArray, ByteArray
val intArray = IntArray(size = 3) // > [0, 0, 0]
val intArrayOf = intArrayOf(0, 1, 2) // > [0, 1, 2] 지정한 값 대입
val intArrayAscending = IntArray(size = 3){ init -> init } // > [0, 1, 2]
val intArrayTwice = IntArray(size = 3){ init -> init * 2} // > [0, 2, 4]
}
자료형Array()를 사용할 때는 파라미터로 배열의 크기와 매 값마다 적용할 함수를 전달한다.
배열 확장함수
코틀린은 확장함수를 지원한다. 확장함수를 통해 배열을 더 용이하게 사용할 수 있다.
공식문서를 통해 모든 멤버 함수와 확장 함수를 확인할 수 있다. 아래에서는 자주 사용하는 확장 함수에 대해 소개한다.
fun main() {
val intArray = IntArray(size = 10)
intArray.sort() // 오름차순 정렬 함수
intArray.sortDescending() // 내림차순 정렬 함수
intArray.forEach{ value -> println("index $value") } // 배열의 모든 값 순환 함수
intArray.forEachIndexed{ index, value -> println("index $index value $value") } // 배열의 모든 값, 인덱스 순환 함수
intArray.filter{ index -> index == 0 } // 입력한 조건에 일치하는 값으로 구성된 리스트 반환 함수
intArray.filterNot { index -> index == 0 } // 입력한 조건에 일치하지 않는 값으로 구성된 리스트 반환 함수
intArray.sliceArray(IntRange(3, 9)) // 인덱스 범위에 따른 배열 반환 함수
intArray.slice(1 .. 3) // 인덱스 범위에 따른 리스트 반환 함수
intArray.sum() // 배열의 모든 값 합 반환 함수
intArray.sumOf{ index -> index * 2} // 조건을 적용한 모든 값 합 반환 함수
intArray.maxOrNull() // 최댓값, 크기가 없는 경우 null 반환 함수
intArray.minOrNull() // 최솟값, 크기가 없는 경우 null 반환 함수
println(intArray.contentToString()) // 배열의 모든 값 출력 함수
}
이외에도 정말 강력한 확장함수를 찾아볼 수 있다. 확장함수를 통해 타입이나 자료구조가 달라도 비슷한 확장함수를 사용할 수 있다. 이것 역시 확장 함수의 장점이다.
다차원 배열
다차원 배열은 배열안에 또 다른 배열을 선언함으로써, 사용할 수 있다.
fun main() {
val charArrayInArray = arrayOf(
charArrayOf('자', '이', '노'),
charArrayOf('도', '미', '닉'),
) // > [['자', '이', '노'], ['도', '미', '닉']]
val intArrayInArray = Array(size = 3){ IntArray(size = 4){ init -> 2 } }
// > [[2, 2, 2, 2], [2, 2, 2, 2], [2, 2, 2, 2]]
}
Kotlin Collections(List, Map, Set), Collections Extension Functions
필자가 kotlin으로 코딩테스트 준비하기 2편에서 kotlin Collections를 따로 작성하였다.
Collections와 Collections Extension Functions는 해당 포스팅에서 확인할 수 있다.
Kotlin ArrayDeque
ArrayDeque란 Kotlin에서 제공하는 Deque이다. Deque는 처음과 끝에 삽입할 수 있고, 반대로 삭제가 가능한 자료구조이다.
Java의 ArrayDeque도 사용할 수 있는데, Kotlin ArrayDeque를 사용하는 것을 추천한다. ArrayDeque의 함수가 다 List와 유사한 코틀린 네이밍으로 되어있어 사용하기에 용이하고, 역시 많은 확장 함수를 사용할 수 있다.
fun main() {
val arrayDeque = ArrayDeque<Int>() // ArrayDeque 선언
// val arrayDequeWithInitialList = ArrayDeque(listOf(1, 2, 3)) // 초기 값 대입 가능
arrayDeque.add(1) // 뒤에 1 추가
arrayDeque.addAll(listOf(2, 3, 4)) // 뒤에 리스트의 값 전부 추가
arrayDeque.addFirst(0) // 앞에 0 추가
arrayDeque.addLast(5) // 뒤에 5 추가. add() 와 같다.
// [0, 1, 2, 3, 4, 5]
arrayDeque.removeFirstOrNull() // 맨 앞 제거
arrayDeque.removeLastOrNull() // 맨 뒤 제거
// [1, 2, 3, 4]
}
처음 블로그 포스팅한 글인데도 불구하고, 많이 봐주셔서 감사합니다. 이후에도 추가할 내용이 떠오르면 추가하겠습니다😊
참고 자료
https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/-array-deque/
ArrayDeque - Kotlin Programming Language
kotlinlang.org
https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-int-array/
IntArray - Kotlin Programming Language
kotlinlang.org