[kotlin]배열과 컬렉션
Int와 Double같은 타입은 모두 하나의 변수에 하나의 값만 저장하도록 되어있는데,
프로그래밍을 하다보면 하나의 변수에 여러개의 값을 저장해야할때가 있다
이처럼 여러개의 값을 하나의 변수에 저장할수있도록 배열(Array)과 컬렉션(Collection)이라는 데이터 타입을 제공한다
# 배열
- 여러개의 값을 담을 수 있는 대표적인 자료형
- 배열 공간의 개수를 할당하거나, 초기화시에 데이터를 저장해주면 데이터의 개수만큼 배열의 크기가 결정됨
- 개수를 정해놓고 사용해야하며, 중간에 개수를 추가하거나 제거할 수 없다
var 변수 = Array(개수)
-> 배열은 다른 데이터 타입과 마찬가지로, 변수에 저장해서 사용할수있으며 위와같은 형태로 선언한다
var students = IntArray(10)
var longArray = LongArray(10)
var CharArray = CharArray(10)
var FloatArray = FloatArray(10)
var DoubleArray = DoubleArray(10)
-> 배열 객체는 Int, Long, Char 등과 같은 타입 뒤에 Array를 붙여서 만든다
- 첫번째 줄은 변수 students에 Int(정수형)공간을 10개 할당하라는 의미
- students라는 이름으로 정수형 데이터를 담을수있는 10개의 공간을 가진 배열이 만들어지고, 각 공간에는 아직 무슨값이 들어있는지 모름
- 인덱스를 사용해서, 10개의 공간을 가지는 위와 같은 배열의 인덱스는 0부터 시작해서 9에서 끝남(컴퓨터는 0을 첫번째로 인식함)
# 문자 배열에 빈공간 할당하기
- String은 Int, Double 등과 같은 기본 타입이 아니기 때문에 StringArray는 없지만, 다음과 같이 사용할 수 있다
var stringArray = Array(10, { item -> "" } )
-> 괄호안의 숫자인 10만 변경해서 사용하면, 그 숫자만큼 빈 문자열로 된 배열공간을 할당한다
# 값으로 배열공간 할당하기
- arrayOf 함수를 사용해서 String값을 직접 할당할수있다
var dayArray = arrayOf("MON", "TUE", "WED", "THU", "FRI", "SAT", "SUN")
# 배열에 값 입력하기
- 배열을 선언한 변수명 옆에 대괄호 [ ] 를 사용하고, 대괄호안에 값을 저장할 위치의 인덱스 번호를 작성한다. 그리고 등호를 사용해서 값을 입력할 수 있다
배열명[인덱스] = 값
- set 함수도 사용할 수 있다
- set 함수에 인덱스와 값을 파라미터로 넘겨준다
배열명.set(인덱스, 값)
다음은 첫번째부터 열번째까지의 인덱스 값을 바꾸는 예제 코드이다
students[0] = 90
students.set(1, 91)
...
sutdents.set(8, 98)
students[9] = 99
-> students의 인덱스 0번째에 90을 넣고, 인덱스 1번째에는 91을 넣는다
-> students의 인덱스 8번째에 98을 넣고, 인덱스 9번째에는 99를 넣는다
# 여기서 잠깐!!
- 배열의 범위를 벗어난 인덱스에 값을 넣을 경우에는 어떻게 될까??
- 아래 예제와 같이, 10개의 공간이 할당된 배열에서 11번째에 해당하는 10번 인덱스에 값을 넣으려고하면, 범위를 넘어섰다는 Exception이 발생하고 프로그램이 종료된다
var intArray = IntArray(10)
intArray[10] = 100 // Exception 발생. intArray의 마지막 인덱스는 9입니다.
--> ArrayIndexOutOfBoundsException 발생
# 배열에 있는 값 꺼내기
- 저장할 때와 마찬가지로, 대괄호안에 인덱스를 입력해서 값을 가져올 수 있으며, get()을 이용할수도 있다
배열명[인덱스]
배열명.get(인덱스)
var seventhValue = intArray[6]
-> 배열 intArray의 일곱번째값을 seventhValue 변수에 저장한다
var tenthValue = intArray.get(9)
-> 배열 intArray의 열번째 값을 get함수를 사용해서 tenthValue변수에 저장한다
아래는 예제 전체 코드이다
package net.flow9.thisiskotlin.basicsyntax
import android.os.Bundle
import android.util.Log
import androidx.appcompat.app.AppCompatActivity
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// 1. 기본타입 배열 선언하기 - 각 기본타입별로 10개의 빈 공간이 할당된다
var students = IntArray(10)
var longArray = LongArray(10)
var CharArray = CharArray(10)
var FloatArray = FloatArray(10)
var DoubleArray = DoubleArray(10)
// arrayOf를 사용하면 선언과 동시에 값을 입력할 수 있다
var intArray = intArrayOf(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
// intArray 변수에는 1 부터 10까지의 값이 각각의 배열공간에 저장되어 있다
// 2. 문자열타입 배열 선언하기
var stringArray = Array(10, { item -> "" })
// arrayOf 함수로 값을 직접 입력해서 배열을 생성할 수 있다
var dayArray = arrayOf("MON", "TUE", "WED", "THU", "FRI", "SAT", "SUN")
// 3. 앞에서 선언한 studens 변수에 값 넣기
// 가. 대괄호를 사용하는 방법
students[0] = 90
students[1] = 91
students[2] = 92
students[3] = 93
students[4] = 94
// 나. set 함수를 사용하는 방법
students.set(5, 95)
students.set(6, 96)
students.set(7, 97)
students.set(8, 98)
students.set(9, 99)
// 4. 값 변경해 보기
intArray[6] = 137 // 6번 인덱스인 7번째 값 7이 137로 변경된다
intArray.set(9, 200) // 9번 인덱스인 10번째 값 10이 200으로 변경된다
// 5. 배열 값 사용하기
var seventhValue = intArray[6]
Log.d("Array", "7번째 intArray의 값은 ${seventhValue}입니다")
var tenthValue = intArray.get(9)
Log.d("Array", "10번째 intArray의 값은 ${tenthValue}입니다")
Log.d("Array", "1번째 dayArray 값은 ${dayArray[0]}입니다")
Log.d("Array", "6번째 dayArray 값은 ${dayArray.get(5)}입니다")
}
}
/** [로그캣 출력 내용]
7번째 intArray의 값은 137입니다
10번째 intArray의 값은 200입니다
1번째 dayArray 값은 MON입니다
6번째 dayArray 값은 SAT입니다
*/
# 컬렉션
- 컬렉션(Collection)은 다른 이름으로는 "동적배열" 이라고도 한다
- 배열과는 다르게, 공간의 크기를 처음크기로 고정하지 않고, 임의의 개수를 담을 수 있기 때문이다
- 컬렉션은 크게 세가지로, 리스트(List), 맵(Map), 셋(Set) 이 있다
# 리스트(List)
- 리스트는 저장되는 데이터에 인덱스를 부여한 컬렉션이며, 중복된 값을 입력할수있다
- Kotlin에서 동적으로 리스트를 사용하기 위해서는 리스트 자료형 앞에 Mutable(뮤터블)이라는 접두어가 붙는다
- 예를 들면, mutableList, mutableMap, mutableSet 이 있다
- 배열과 같이 '데이터 타입Of" 형태로 사용할 수 있다
var list = mutableListOf("MON", "TUE", "WED")
# 여기서 잠깐!!
- Mutable(뮤터블)이란??
- Mutable(뮤터블)이란 "변할 수 있다" 라는 의미를 가지고 있다
- kotlin은 컬렉션 데이터 타입을 설계할때 모두 immutable(이뮤터블: 변할수없는)으로 설계했다. 그래서 기본 컬렉션인 리스트(List), 맵(Map), 셋(Set)은 모두 한번 입력된 값을 바꿀 수 없다
- 그래서 컬렉션의 원래 용도인 동적배열!! 로 사용하기 위해서는 Mutable(뮤터블)로 만들어진 데이터 타입을 사용해야한다
# 리스트 생성하기 : mutableListOf
- 다음과 같이 작성하면, 변수에 "MON", "TUE", "WED" 의 3개의 값을 가진 크기가 3인 동적 배열 리스트가 생성된다
var mutableList = mutableListOf("MON", "TUE", "WED)
# 리스트에 값 추가하기 : add
- add 함수를 사용해서 값을 추가할수있다
- 값이 추가되면, 동적으로 리스트의 공간이 자동으로 증가된다
mutableList.add("THU")
- add 함수를 사용하면, 입력될 위치인 인덱스를 따로 지정해주지 않아도 입력되는 순서대로 인덱스가 지정된다
-> "MON": [0], "TUE": [1], "WED": [2] --> "MON": [0], "TUE": [1], "WED": [2], "THU": [3]
# 리스트에 입력된 값 사용하기 : get
- get함수로 리스트에서 값을 꺼내서 사용할 수 있다
- 입력과는 다르게, 입력된 값을 사용할때는 인덱스를 지정해서 몇번째 값을 꺼낼것인지를 명시해야한다
var variable = mutableList.get(1)
-> 두번째 값을 꺼내서 variable이라는 변수에 저장한다
# 리스트값 수정하기 : set
- set함수를 사용해서 특정 인덱스의 값을 수정할 수 있다
mutalbeList.set(1, "수정")
-> 두번째 값을 "수정" 으로 수정했다
# 리스트에 입력된 값 제거하기 : removeAt
- removeAt 함수로 리스트에 입력된 값의 인덱스를 지정해서 삭제할 수 있다
mutableList.removeAt(1)
-> 두번째 값을 삭제했다
-> 두번째 값을 삭제하면, 세번째 값부터 인덱스가 하나씩 감소하면서 빈자리의 인덱스로 이동한다
-> "MON": [0], "TUE": [1], "WED": [2] --> "MON": [0], "WED": [1]
-> "TUE"가 삭제되면서 3번째값인 "WED"의 인덱스는 2에서 1로 바뀐다
# 빈 리스트 사용하기
- 아무것도 없는 빈 리스트를 생성하면, 앞으로 입력되는 값의 데이터타입을 알 수 없기 때문에 값의 타입을 추론할 수 없다
- 그래서 빈 컬렉션의 경우, 앞에서처럼 " 데이터타입Of "만으로는 생성되지 않고, 데이터타입을 직접적으로 알려주는 방법을 사용해야한다
var 변수명 = mutableListOf<컬렉션에 입력될 값의 타입>()
var stringList = mutableListOf<String>()
// 생성
var stringList = mutableListOf<String>()
// 입력
stringList.add("월")
stringList.add("화")
// 사용
Log.d("Collection", "stringList에 입력된 두 번째 값은 ${stringList.get(1)}입니다.")
// 수정
stringList.set(1, "수정된 값")
// 삭제
stringList.removeAt(1) // 두 번째 값이 삭제 됩니다.
-> 문자열로 된 빈 리스트를 생성하고 조작할 수 있다
# 여기서 잠깐!!
- 제네릭(Generic) 이란??
- 리스트 컬렉션을 생성하면서 < > 괄호를 사용하는데, 이 괄호를 제네릭(Generic)이라고 한다
- 제네릭(Generic)은 컬렉션이 사용하는 값의 타입을 지정하기 위한 도구이다
- kotlin에서 컬렉션은 제네릭을 사용하지 않으면 사용할 수 없다
- 단, 값으로 초기화할때는 입력되는 값으로 타입을 추론할 수 있기때문에 이때는 제네릭을 쓰지 않아도 생성할 수 있다
# 컬렉션 개수 가져오기 : size
- size 프로퍼티를 사용하면 컬렉션의 개수를 가져올 수 있다 (mutableList.size)
- 위에서 set, get 등은 "함수" 라고 하고, size는 "프로퍼티"라는 용어를 사용했는데, 이 둘을 구분하는 방법은 괄호의 유무이다
- 괄호가 있으면 -> 함수
- 괄호가 없으면 -> 프로퍼티
package net.flow9.thisiskotlin.basicsyntax
import android.os.Bundle
import android.util.Log
import androidx.appcompat.app.AppCompatActivity
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// 1. 값으로 컬렉션 생성하기
var mutableList = mutableListOf("MON", "TUE", "WED")
// 값 추가하기
mutableList.add("THU")
// 값 꺼내기
Log.d("Collection", "mutableList의 첫 번째 값은 ${mutableList.get(0)}입니다")
Log.d("Collection", "mutableList의 두 번째 값은 ${mutableList.get(1)}입니다")
// 2. 빈 컬렉션 생성하기기
var stringList = mutableListOf<String>() // 문자열로 된 빈 컬렉션 생성
// 값 추가하기
stringList.add("월")
stringList.add("화")
stringList.add("수")
// 값 변경하기
stringList.set(1, "날짜 변경")
// 사용
Log.d("Collection", "stringList에 입력된 두 번째 값은 ${stringList.get(1)}입니다")
// 삭제
stringList.removeAt(1) // 두 번째 값이 삭제된다.
Log.d("Collection", "stringList에 입력된 두 번째 값은 ${stringList.get(1)}입니다")
}
}
/** [로그캣 출력 내용]
mutableList의 첫 번째 값은 MON입니다
mutableList의 두 번째 값은 TUE입니다
stringList에 입력된 두 번째 값은 날짜 변경입니다
stringList에 입력된 두 번째 값은 수입니다
*/
-> 컬렉션 List를 사용한 예제이다
# 셋 (Set) - mutableSetOf
- set은 "중복을 허용하지 않는" 리스트이다
- 리스트와 유사한 구조이지만 인덱스로 조회할 수 없고, get함수도 지원하지 않는다
- String 타입의 값을 입력받기 위해 다음과 같이 선언할 수 있다
var set = mutableSetOf<String>()
# 빈 Set으로 초기화하고 값 입력하기
- set은 중복을 허용하지 않기 때문에 아래 코드를 보면, 네번째줄에서 입력한 "JAN"은 입력되지 않는다
var set = mutableSetOf<String>()
set.add("JAN")
set.add("FEB")
set.add("MAR")
set.add("JAN") // 동일한 값은 입력되지 않습니다.
# Set의 값 인덱싱?
- set은 인덱스로 조회하는 함수가 없기 때문에, 특정위치의 값을 사용할 수 없다
# Set의 값 삭제하기
- set은 값이 중복되지 않기 때문에, 아래와같이 값을 직접 입력하여 삭제해야된다
set.remove("FEB")
package net.flow9.thisiskotlin.basicsyntax
import android.os.Bundle
import android.util.Log
import androidx.appcompat.app.AppCompatActivity
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// 1. 셋 생성하고 값 추가하기
var set = mutableSetOf<String>()
set.add("JAN")
set.add("FEB")
set.add("MAR")
set.add("JAN") // 동일한 값은 입력되지 않는다.
// 2. 전체 데이터 출력해보기
Log.d("Collection", "Set 전체출력 = ${set}")
// 3. 특정 값 삭제하기
set.remove("FEB")
Log.d("Collection", "Set 전체출력 = ${set}")
}
}
/** [로그캣 출력 내용]
Set 전체출력 = [JAN, FEB, MAR]
Set 전체출력 = [JAN, MAR]
*/
-> 컬렉션 Set을 사용한 예제이다
# 맵(Map) - mutableMapOf
- Map은 키(key)와 값(Value)의 쌍으로 입력되는 컬렉션이다
- Map의 키는 리스트의 인덱스와 비슷한데, Map에서는 키를 직접 입력해야한다
- 제네릭으로 키와 값의 데이터타입을 지정해서 맵을 생성한다
var map = mutableMapOf<String, String>()
-> 키와 값의 타입을 모두 String으로 사용하기 위한 예제 코드이다
-> 인덱스에 해당하는 키를 직접 지정해서 사용해야한다
# 빈 Map으로 생성하고 값 추가하기
- 값을 추가하기 위해, 맵에서 제공되는 put함수를 사용해서 키와 값을 입력하면 된다
var map = mutableMapOf<String, String>()
map.put("key1", "value2")
map.put("key2", "value2")
map.put("key3", "value3")
-> 키와 값을 추가할때마다 리스트처럼 맵의 공간이 늘어난다
# Map 사용하기
- get 함수에 키를 직접 입력해서 값을 꺼낼 수 있다
Log.d("CollectionMap", "map에 입력된 key1의 값은 ${map.get("key1")}입니다.")
// [로그캣 출력 내용]
// map에 입력된 key1의 값은 value2입니다.
# Map 수정하기
- 입력할때와 같이 put함수를 사용해서 수정하는데 동일한 키를 가진값이 있으면, 키는 유지된 채로 그 값만 수정된다
map.put("key2", "수정")
-> 기존 "key2"의 값인 "value2"는 "수정"으로 바뀐다
# Map 삭제하기
- remove함수에 키를 입력해서 값을 삭제할 수 있다
- 리스트와는 다르게 인덱스에 해당하는 키의 값이 변경되지 않고, 그대로 유지된다
(삭제된다고해서 남아있는 인덱스의 값이 앞으로 옮겨가지 않음, 삭제되면 null값이 됨)
package net.flow9.thisiskotlin.basicsyntax
import android.os.Bundle
import android.util.Log
import androidx.appcompat.app.AppCompatActivity
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// 1. 맵 생성하기
var map = mutableMapOf<String, String>()
// 2. 값 넣기
map.put("키1", "값2")
map.put("키2", "값2")
map.put("키3", "값3")
// 3. 값 사용하기
var variable = map.get("키2")
Log.d("Collection", "키2의 값은 ${variable}입니다")
// 4. 값 수정하기
map.put("키2", "두 번째 값 수정")
Log.d("Collection", "키2의 값은 ${map.get("키2")}입니다")
// 5. 값 삭제하기
map.remove("키2")
// 5.1 없는 값을 불러오면 null값이 출력된다
Log.d("Collection", "키2의 값은 ${map.get("키2")}입니다")
}
}
/** [로그캣 출력 내용]
키2의 값은 값2입니다
키2의 값은 두 번째 값 수정입니다
키2의 값은 null입니다
*/
-> 컬렉션 Map을 사용한 예제
# 여기서 잠깐!!
- 컬렉션 값의 단위 = 엘리먼트
- 컬렉션에 입력되는 값 각각을 엘리먼트(Element) 라고 한다
- 값이라고 해도 되지만 맵을 지칭할 때 맵의 값(엘리먼트 자체)를 가리키는건지, 엘리먼트의 값(실제값)을 가리키는 건지 2개의 용어가 충돌할 수 있기 때문에 엘리먼트라고 이해하는것이 좋다
- 엘리먼트는 맵의 입력단위인 키와 값을 합친것을 말하는데, 이것은 리스트와 셋에서도 동일한 용어로 사용된다
- 즉, 리스트의 값 또한 엘리먼트라고 부른다
- -> 리스트 엘리먼트 = 리스트의 (값)
- -> 맵 엘리먼트 = 맵의 (키와 값)
# 이뮤터블 컬렉션 (Immutable Collection)
- kotlin은 일반 배열처럼 크기를 변경할 수 없으면서, 값 또한 변경할 수 없는 "이뮤터블 컬렉션"을 지원한다
- 이뮤터블 컬렉션은 다음과 같이 기존 컬렉션에서 mutable이라는 접두어가 제거된 형태로 사용된다
var list = listOf("1", "2")
- 불변형 컬렉션은 한번 입력된 값을 변경할 수 없기 때문에, add나 set함수는 지원하지 않고, 최초 입력된 값만을 사용할 수있다
- 배열과 다른 점은 크기뿐만 아니라, 값의 변경 또한 불가능하다는 것이다
- 즉, 불변형 컬렉션은 수정, 추가, 제거가 모두 안된다
var immutableList = listOf("JAN", "FEB", "MAR") // 생성
Log.d("Collection", "리스트의 두 번째 값은 ${immtuableList.get(1)}입니다.) // 사용
// [로그캣 출력 내용]
// 리스트의 두 번째 값은 FEB입니다.
# 그러면 이뮤터블 컬렉션은 언제 사용할 수 있을까??
- 일반변수 var과 읽기전용변수 val의 관계에서 이 사용법을 유추할 수 있는데, 기준이 되는 어떤 값의 모음을 하나의 변수에 저장할 필요가 있거나 또는 여러개의 값을 중간에 수정하지 않고 사용할 필요가 있을때 이뮤터블 컬렉션을 사용한다
- 대표적인 예로 요일 데이터가 있다
- -> 아래처럼 7개의 요일을 이뮤터블 리스트로 선언하면, 중간에 바뀌지 않기 때문에 계속 같은 값을 유지하면서 사용할 수 있다
var dayList = listOf("월", "화", "수", "목", "금", "토", "일)