라떼는말이야

[Kotlin] 코틀린의 collections-List, Set, Map (파이썬과 비교) 본문

공부

[Kotlin] 코틀린의 collections-List, Set, Map (파이썬과 비교)

MangBaam 2021. 7. 24. 17:32
반응형

Collections

대부분의 언어에는 Collections라는 개념이 존재한다.

Collection이란 일반적으로 같은 타입의 Object를 포함한다. (0개를 포함할 수도 있다) 물론 언어마다 조금씩 다른 특징을 가진다.

 

Collection에 들어간 Object는 elements(요소)나 items으로 불린다.

 

코틀린의 Collection type

코틀린에서는 3 종류의 Collection type이 있다.

  • List
  • Set
  • Map

List

인덱스로 접근 가능한 순서가 있는 collection이다.

요소들은 중복으로 들어갈 수 있다.

파이썬의 Tuple과 비슷하다. (뒤에 나오는 MutableList는 파이썬의 List와 비슷하다)

Set

유일한 elements의 collection이다.

즉, List처럼 요소가 반복적으로 등장할 수 없다. (요소 중복 불가)

일반적으로, Set의 요소들의 순서가 중요하지 않다.

파이썬의 Set과 비슷하다.

Map(or dictionary)

key-value(키-값) 쌍이다.

키는 유일하며 각 키들이 정확히 하나의 값과 매핑된다.

키는 유일하지만 값들은 중복이 될 수 있다.

파이썬의 Dict와 비슷하다.

 

예를 들어 키가 "one" 일 때 값이 "1", 키가 "하나" 일 때도 값이 "1"이 될 수 있지만

키가 "2"일 때 값이 "two", 키가 "2"일 때 값이 "둘" 이면 키가 중복이기 때문에 불가능하다.

 

코틀린 Collection interfaces의 2가지 종류

코틀린의 Collections는 Mutable 타입과, Immutable 타입 중 하나이다.

(코틀린 공식 Doc에서는 Immutable 타입을 read-only 라고 표현하는 듯 하다)

코틀린 collections의 인터페이스 관계도

다이어그램을 보면 가장 위, 오른쪽에 Iterable 인터페이스가 보인다.

Iterable은 순회를 할 수 있는 특징이다.

그리고 그 왼쪽으로 MutableIterable 인터페이스가 Iterable 인터페이스를 상속해 존재하는데 Mutable이라는 말은 수정을 할 수 있는 속성이다.

즉, Collections의 요소를 추가, 변경하거나 삭제하는 등의 수정이 가능한 특징이다.

 

그렇기 때문에 Collection 인터페이스를 상속한 List와 Set은 수정이 불가능Immutable이고,

MutableCollection 인터페이스를 상속한 MutableList와 MutableSet은 수정이 가능한 Mutable Collection이다.

 

추가로 Iterable 인터페이스를 상속하진 않았지만 Map도 Immutable한 성질을 가지는 Map과 수정 가능한 MutableMap이 존재한다.


참고로 수정이 가능한 mutable속성에서 val로 변수를 선언해도 괜찮다.

예를 들어 다음과 같은 경우이다.

val numbers = mutableListOf("one", "two", "three", "four")
numbers.add("five")		// 가능한 연산이다.
// numbers = mutableListOf("six", "seven") 		// 불가능한 연산이다.

요소를 변경한다고 해도 같은 object이지에 참조는 변경되지 않는다.

그러나 val로 선언된 collection 변수에 재할당하려고 한다면 에러가 발생한다.


 

 

 

 

사용 방법

List

코틀린

// listOf()로 리스트 생성
val numbers = listOf("one", "two", "three", "four")

// .size 로 리스트 크기 반환
println("Number of elements: ${numbers.size}")

// .get(n) 로 특정 인덱스의 요소 반환
println("Third element: ${numbers.get(2)}")

// [n] 로 특정 인덱스의 요소 반환
println("Fourth element: ${numbers[3]}")

// .indexOf(요소) 로 특정 요소의 인덱스 반환
println("Index of element \"two\" ${numbers.indexOf("two")}")

파이썬

numbers = ("one", "two", "three", "four") # Tuple 생성

print(f"Number of elements: {len(numbers)}")

# 파이썬은 .get 메소드가 없다.

print(f"Fourth element: {numbers[3]}")

print("Index of element \"two\" {}".format(numbers.index("two")))

파이썬에는 List와 MutableList가 구분되어 있지 않고, 파이썬에서 list는 수정이 가능하다.

하지만 파이썬에는 Tuple이 있다.

코틀린의 List와 파이썬의 Tuple이 비슷한 개념이라고 생각해도 될 것 같다.

 

(코틀린) 리스트 비교

data class Person(var name: String, var age: Int)

val bob = Person("Bob", 31)

val people = listOf(Person("Adam", 20), bob, bob)
val people2 = listOf(Person("Adam", 20), Person("Bob", 31), bob)

println(people == people2)	// true
bob.age = 32
println(people == people2)	// false

만약 두 리스트가 같은 크기와 같은 위치에 구조적으로 같은 요소를 가지고 있다면 두 리스트는 같다. ( == 로 비교)

 

MutableList

코틀린

val numbers = mutableListOf(1, 2, 3, 4) // mutableList 생성

// 요소 추가
numbers.add(5)

// 특정 인덱스의 요소 삭제
numbers.removeAt(1)

// 특정 인덱스 값 수정
numbers[0] = 0

// 리스트 요소 순서 섞기
numbers.shuffle()

println(numbers)

파이썬

import random # shuffle이 정의 된 라이브러리

numbers = [1, 2, 3, 4]

numbers.append(5)

numbers.remove(1)

numbers[0] = 0

random.shuffle(numbers)

print(numbers)

 

※ 리스트와 배열의 차이점:

리스트와 배열은 성질이 꽤 비슷하다. 하지만 가장 큰 차이점은 배열은 정의 및 초기화 될 때 크기가 정해지고 변하지 않는다는 점이다.

리스트는 미리 정의된 크기가 없다. 그렇기에 더하고, 수정하고, 제거하는 연산의 결과에 의해 크기가 변할 수 있다.

 

Set

코틀린

val numbers = setOf(1, 2, 3, 4)

println("Number of elements: ${numbers.size}")
// Number of elements: 4

if (numbers.contains(1)) println("1 is in the set")
// 1 is in the set

val numbersBackwards = setOf(4, 3, 2, 1)

println("The sets are equal: ${numbers == numbersBackwards}") 
// The sets are equal: true

코틀린에서 set은 setOf() 로 만들 수 있다.

set은 순서가 중요하지 않기 때문에 두 set의 크기가 같고, set을 이루는 각 요소 구성이 같다면 두 set은 같은 set이다.

 

파이썬

numbers = {1, 2, 3, 4}

print(f"Number of elements: {len(numbers)}")

if 1 in numbers: print("1 is in the set")

numbersBackwords = {4, 3, 2, 1}

print(f"the sets are equal: {numbers == numbersBackwords}")

파이썬 역시 크기가 같고, 같은 구성의 set을 같다고 판단한다.

 

코틀린

val numbers = setOf(1, 2, 3, 4)  // LinkedHashSet is the default implementation
val numbersBackwards = setOf(4, 3, 2, 1)

println(numbers.first() == numbersBackwards.first()) // false
println(numbers.first() == numbersBackwards.last()) // true

그러나 코틀린에서 set은 LinkedHashSet로 구현되기 때문에 요소가 삽입된 순서를 보존할 수 있다.

그렇기에 first(), last()와 같은 메소드로 인덱싱이 가능하다.

 

 

 

 

Map

Map<K, V>은 Collection 인터페이스를 상속하지 않지만 코틀린 collection type은 맞다.

코틀린

val numbersMap = mapOf("key1" to 1, "key2" to 2, "key3" to 3, "key4" to 1)

println("All keys: ${numbersMap.keys}")
// All keys: [key1, key2, key3, key4]

println("All values: ${numbersMap.values}")
// All values: [1, 2, 3, 1]

if ("key2" in numbersMap) println("Value by key \"key2\": ${numbersMap["key2"]}")
// Value by key "key2": 2

if (1 in numbersMap.values) println("The value 1 is in the map")
// The value 1 is in the map

if (numbersMap.containsValue(1)) println("The value 1 is in the map") // same as previous
// The value 1 is in the map

파이썬

numbersMap = {"key1": 1, "key2": 2, "key3": 3, "key4": 4}

print(f"All keys: {numbersMap.keys()}")
print(f"All values: {numbersMap.values()}")
if "key2" in numbersMap: print("Value by key \"key2\": {}".format(numbersMap["key2"]))
if 1 in numbersMap.values(): print("The value 1 is in the map")

코틀린

val numbersMap = mutableMapOf("one" to 1, "two" to 2)
numbersMap.put("three", 3)
numbersMap["one"] = 11

println(numbersMap)
// {one=11, two=2, three=3}

코틀린에서는 Map이 수정이 가능한 Mutable과 불가능한 Immutable 로 나누어져 있는데 반해

파이썬은 수정이 불가능한 Dict는 없다.


이 외에도 파이썬의 collections와 다른 점은

파이썬에서는 list, set, dict에 하나의 자료형 뿐만 아니라 다양한 종류의 자료형을 섞어서 구성할 수 있지만 코틀린에서는 하나의 자료형만 넣을 수 있다.

반응형
Comments