Study/다재다능 코틀린 프로그래밍
Day 5. 컬렉션 사용하기
주지민
2021. 10. 31. 19:03
반응형
본 포스트는 지인들과 스터디한 내용을 정리한 포스트입니다
http://www.kyobobook.co.kr/product/detailViewKor.laf?mallGb=KOR&ejkGb=KOR&barcode=9788931463422
1. 컬렉션 사용하기
- Java에서 사용 가능한 컬렉션은 코틀린에서 바로 사용 가능하다
- 그 외에 추가적으로 컬렉션에 대한 몇 가지 뷰 인터페이스를 제공한다
2. 컬렉션 특징
- Java의 뮤터블 컬렉션 인터페이스는 코틀린에서 이뮤터블 읽기전용 인터페이스와 뮤터블 읽기-쓰기 인터페이스로 분리됐다
- 지원되는 대표 컬렉션 종류
- Pair: 값이 두 개인 튜플
- Triple: 값이 세 개인 튜플
- Array: 객체나 Primitive 타입으로 구성되어 순번이 있고, 크기가 고정된 컬렉션
- List: 객체들이 정렬된 컬렉션
- Set: 객체들이 정렬되지 않은 컬렉션
- Map: 연관 사전 혹은 키와 값의 맵
- 코틀린 스탠다드 라이브러리에 포함된 kotlin.collections 패키지에 Java 컬렉션에 유용한 함수들이 많이 추가되었다
- 예를 들어 기존 Java의 for-each는 index를 활용할 수 없는 단점이 있었지만 코틀린의 withIndex() 메서드를 통해 인덱스와 값 모두를 사용할 수 있다
// extension.kts // kotlin style val names = listOf("Tom", "Jerry") println(names.javaClass) for (index, value) in names.withIndex()) { println("$index $value") }
- listOf() 메서드를 이용해 ArrayList 객체를 만들고, withIndex() 메서드를 통해 인덱스와 값을 가져왔다
- withIndex() 메서드는 IndexedValue라는 특별한 반복자를 리턴한다 ( data class )
- 뷰
- 이뮤터블 컬렉션은 동시성을 사용하는 함수형 프로그래밍 또는 비동기 처리를 하는 어플리케이션을 사용할 때 훨씬 안정적이다
- 리스트, 셋, 맵은 각기 뷰를 두 가지 씩 가지고 있다
- 읽기전용 뷰로 통하는 뮤터블 뷰
- 읽기-쓰기 뷰로 불리기도 하는 뮤터블 뷰
- 해당 뷰들을 이용하면 런타임시 오버헤드가 없고, 컴파일 시간이나 실행 시간에 변환이 발생하지 않는다
- 읽기 전용 뷰에 쓰기 시도를 할 경우 컴파일 단계에서 실패
- 예를 들어 List의 읽기전용 뷰인 List, 쓰기전용 뷰인 MutableList가 있다
- 읽기전용 뷰는 쓰레드 안정성을 제공하지 않는다
3. 페어와 트리플 사용하기
- 튜플은 작고 셀 수 있는 크기의 객체 배열이다
- 코틀린은 두 가지 사이즈의 튜플만 허용한다 ( 2개: Pair, 3개: Triple )
- 2개 혹은 3개의 객체를 컬렉션으로 만들고 싶을때 유용하다
- 페어 생성 예제
// createPair.kts val myPair = Pair("Tom", "Jerry") println(myPair) // (Tom, Jerry) println(myPair.javaClass) // class kotlin.Pair val myPairOfMap = mapOf("Tom" to "Cat", "Jerry" to "Mouse") println(myPairOfMap) // {Tom=Cat, Jerry=Moude} println(myPairOfMap.javaClass) // class java.util.LinkedHashMap
- Pair의 생성자를 통해 인스턴스를 생성했다
- to() 확장 함수를 통해 Map의 엔트리가 될 페어를 만들었다
- to() 확장 함수는 코틀린의 모든 객체에서 사용이 가능하다
- 코틀린의 페어는 각기 다른 타입의 객체를 컬렉션으로 만들 때 타입 안정성을 제공해주고 코드의 혼란을 줄여준다
- Java에서는 String, Double 타입 속성을 갖게하려면 Object 부모클래스로 배열을 만들거나 두 타입을 갖는 새로운 클래스를 만들어야하는 불편함이 있었다
- 예제코드
// airporttemperatures.kts val airportCodes = listOf("LAX", "SFO", "PDX", "SEA") val temperatures = airportCodes.map { code -> code to mappingValue(code) } println(temperatures) fun mappingValue(code: String): String { return "$code hi" }
- 페어와 트리플 모두 이뮤터블이다
- 만약 3개보다 더 많은 이뮤터블 값들을 그룹핑하고 싶다면 데이터 클래스를 만드는 것을 고려해 보도록 해라
4. 객체 배열과 Primitive 배열
- Array<T> 클래스는 코틀린의 배열을 상징한다
- 배열은 낮은 수준의 최적화가 필요할 때만 사용하도록하고, 그 외에는 List같은 다른 자료구조를 사용하다
- 배열을 만드는 가장 쉬운 방법은 arrayOf()라는 최상위 함수를 사용하는 것
- 배열을 만들면 인덱스 연산자 []를 이용해서 요소에 접근할 수 있다
- 예제 코드
// arrays.kts val friends = arrayOf("Tintin", "Snowy", "Haddock", "Calculus") println(friends::class) // class kotlin.Array println(friends.javaClass) // class [Ljava.lang.String; println("${friends[0]} and ${friends[1]}") // Tintin and Snowy
- 배열의 타입은 kotlin.Array이지만 실제 JVM에서 실행될 떄 적용되는 진짜 타입은 Java.String이다
- 실제 arrayOf() 메서드를 통해 Array<T>를 생성하면 Primitive Type이라도 Boxing된 클래스 타입으로 생성된다 ( Integer, Double, Long ... ), 이는 불필요한 오버헤드를 발생시키므로 intArrayOf() 메서드 함수등 특수한 함수를 이용하자 ( int 배열 )
- 배열을 만들 때 값을 계산해서 생성할 수 있다
// calculateArrays.kts println(Array(5) { i -> (i + 1) * (i + 1) }.sum())
5. 리스트 사용하기
- 리스트를 만드는 첫 단계에서 코틀린은 개발자에게 이뮤터블 또는 뮤터블인지를 선택하도록 한다
- 이뮤터블 리스트를 만들려면 listOf() 메서드를 사용하면 된다
- 정말 꼭 뮤터블 리스트를 만들어야한다면 mutableListOf() 메서드를 이용해 생성하면 된다 ( 뮤터블은.. 권장 X )
- listOf() 함수는 kotlin.collections.List<T> 인터페이스 참조를 리턴한다
- 예제 코드
// lists.kts val fruites: List<String> = listOf("Apple", "Banana", "Grape") println(fruites) // [Apple, Banana, Grape]
- 값을 가져오려면 인덱스 연산자 [] 혹은 get() 메서드 둘 다 가능하다 ( 인덱스 연산자 [] 또한 내부적으로 get() 메서드를 호출한다 )
- 인덱스 연산자를 사용하는 편이 get() 보다 노이즈가 적고 편리하다
- 리스트가 특정 값을 가지고 있는지를 확인하려면 contain() 메서드 혹은 in을 사용하면 된다
// lists.kts println(fruites.contains("Apple")) println("Apple" in fruites)
- listOf()가 리턴하는 참조를 사용할 때 리스트를 변경할 수는 없다
- kotlin.collections.List<T>의 인터페이스는 컴파일 시간에 Java에서 많이 사용했을 Arrays.asList()로 만든 JDK 객체의 뷰로 동작하지만 이 인터페이스는 변화를 허용하지 않는다 ( 이뮤터블이다 )
- 기존의 이뮤터블 리스트를 변화하지 않으면서 요소를 추가하여 새로운 리스트를 반환하는데 편리한 '+'를 쓸 수 있다
// lists.kts val fruits2 = fruits + "Orange" println(fruits) println(fruits2)
- 합리적인 추론으로 '-"도 있지 않을까 생각되면 당연히 있다 ( '-': 요소를 제거하여 새로운 리스트 반환 )
6. 셋 사용하기
- 셋은 정렬되지 않은 요소의 모음이다
- 이뮤터블/읽기전용 버전과 뮤터블/읽기-쓰기 버전 모두가 있다
- Set<T> 인스턴스를 만들기 위해서는 setOf() 메서드를 사용, MutableSet<T>를 만들기 위해서는 mutableSetOf()를 사용하면 된다
// sets.kts val fruits: Set<String> = setOf("Apple", "Banana", "Apple") println(fruits) // [Apple, Banana] println(fruits::class) // class java.util.LinkedHashSet println(fruits.javaClass) // class java.util.LinkedHashSet
- set<T>와 MutableSet<T>에는 위에서 소개된 List처럼 +, -, contains, in 등 많은 함수가 포함되어있다
7. 맵 사용하기
- 맵은 키-값 페어를 보관하는 콜렉션이다
- 기타 컬렉션들과 마찬가지로 읽기전용과 읽기-쓰기용 인터페이스를 제공한다
- mapOf()를 사용해서 Map<K,V>의 읽기전용 맵을 만들 수 있다
- mutableMapOf()를 사용하면 MutableMap<K,V>의 읽기-쓰기 맵을 만들 수 있다
- JDK의 참조
- HashMap -> hashMapOf()
- LinkedHashMap -> linkedMapOf()
- SortedMap -> sortedMapOf()
- 예제코드
// usingmap.kts val sites = mapOf( "pragprog" to "https://www.pragprog.com", "agiledeveloper" to "https://agiledeveloper.com" ) println(sites) // {pragprog=https://www.pragprog.com, agiledeveloper=https://agiledeveloper.com} println(sites.size) // 2 println(sites::class) // class java.util.LinkedHashMap println(sites.javaClass) // class java.util.LinkedHashMap
- 키-값 페어가 to() 확장함수를 통해서 만들어진다
- to() 확장함수는 코틀린의 모든 객체에서 사용 가능하고, mapOf()는 Pair<K,V>를 인자로 취급한다
- keys 속성은 맵에 존재하는 모든 키를, values는 맵에 존재하는 모든 값을 반복할 수 있다
- containsKey(), containsValue() 는 각각 키와 값이 존재하는지 체크할 수 있고, 키 존재 여부는 contains(), in으로도 가능하다
- 키로 맵에서 값을 꺼내올 때는 get() 메서드 혹은 인덱스 연산자 []를 이용할 수 있는데 키가 맵에 없을 경우 nullable 타입을 리턴한다
// usingmap.kts val pragProgSite: String? = sites.get("pragprog") val pragProgSite2: String? = sites["pragprog"] println(pragProgSite) println(pragProgSite2)
- 이런 nullable 타입을 피하기 위해 키가 없으면 정해진 기본값을 리턴하게 할 수 있다 ( getOrDefault )
// usingmap.kts val agiledeveloper = sites.getOrDefault("agiledeveloper", "http://www.example.com") println(agiledeveloper) println(agiledeveloper::class) // class kotlin.String println(agiledeveloper.javaClass) // class java.lang.String
- 위에서 소개한 리스트, 셋과 마찬가지로 '+', '-'를 이용해서 요소를 추가한, 제거한 새로운 맵을 얻을 수 있다
728x90
반응형