👩🏻💻 맵은 키 Key와 값 Value의 쌍으로 입력되는 컬렉션 👩🏻💻 맵의 키는 리스트의 인덱스와 비슷한데 맵에는 키를 직접 입력하고 중복되지 않도록 해야 함
⚡️ 제네릭으로 키와 값의 데이터 타입을 지정해서 맵을 생성 ⚡️ 맵의 요소는 Pair(A, B)를 사용하는 데 이는 A to B로도 표현할 수 있음
// 읽기 전용 맵
val immutableMap = mapOf("name" to "tom", "age" to 28, "age" to 20, "height" to 170)
println(immutableMap) // {name=tom, age=20, height=170} 중복이 있을 경우 마지막 키값이 저장됨
자바에서 리스트 List, 셋 Set, 맵 Map 등 여러 자료 구조를 사용하는데, 코틀린에서도 이러한 컬렉션을 모두 사용할 수 있을 뿐만 아니라 몇 가지 편리한 함수를 추가로 제공. 또한 코틀린은 컬렉션을 읽기 전용 immutable 컬렉션과 읽기-쓰기 mutable 컬렉션으로 크게 두 가지로 나눔
1) 리스트 List
👻 리스트 List는 저장되는 데이터에 인덱스를 부여한 컬렉션이며 중복된 값을 입력할 수 있음 👻 코틀린에서 동적으로 리스트를 사용하기 위해서는 리스트 자료형 앞에 뮤터블 Mutable 이라는 접두어 prefix가 붙음 💫 접두어가 있는 리스트도 있지만 잘 사용하지 않기 때문에 항상 mutableList, mutableMap, mutableSet을 사용
뮤터블과 이뮤터블
프로그래밍 언어에서 뮤터블은 변할 수 있는 데이터 타입을 가르키는 용어. 변수로는 var이 뮤터블 그리고 반대 개념인 이뮤터블 Immutable이 있는데 이것은 val과 같이 변할 수 없는 데이터 타입을 가르키는 용어.
⚡️ 코틀린은 컬렉션 데이터 타입을 설계할 때 모두 이뮤터블로 설계 ⚡️ 기본 컬렉션인 리스트, 맵, 셋은 모두 한 번 입력된 값을 바꿀 수 없음 ⚡️ 컬렉션의 원래 용도인 동적 배열로 사용하기 위해서는 뮤터블로 만들어진 데이터 타입을 사용해야 함
읽기 전용 리스트는 listOf() 함수를 사용 읽기 쓰기 모두 가능한 리스트는 mutableListOf() 함수를 사용
// 뮤터블
val mutableList = mutableListOf("MON", "TUE", "WED")
// 3개의 값을 가진 크기가 3인 동적 배열 리스트가 생성
2) 리스트에 값 추가하기 : add
👻 mutableList 변수에 add 메서드를 사용해서 값을 추가 👻 값이 추가되면서 동적으로 리스트의 공간이 자동으로 증가
mutableList.add("THU")
// 입력될 위치인 인덱스를 따로 지정해주지 않아도 입력되는 순서대로 인덱스가 지정.
println(mutableList)
// [MON, TUE, WED, THU]
3) 리스트에 입력된 값 사용하기 : get
👻 입력할 때와는 다르게 사용할 때는 인덱스를 지정해서 몇 번째 값을 꺼낼 것인지 명시
var variable = mutableList.get(1) // 두 번째 값을 변수에 저장
variable = mutableList[1]
println(variable) // TUE
4) 리스트 값 수정하기 : set
👻 특정 인덱스 값을 수정
mutableList.set(1, "수정할 값") // 두 번째 값을 수정
println(mutableList) // [MON, 수정할 값, WED, THU]
mutableList[1] = "수정할 값" // 두 번째 값을 수정.
5) 리스트에 입력된 값 제거하기 : removeAt
👻 리스트에 입력된 값의 인덱스를 지정해서 삭제
println(mutableList.get(1)) // 수정할 값
mutableList.removeAt(1) // 두 번째 값을 삭제
// 두 번째 값을 삭제하면 세 번째 값부터 인덱스가 하나씩 감소하면서 빈자리의 인덱스로 이동
println(mutableList.get(1)) // WED
6) 빈 리스트 사용하기
👻 아무것도 없는 빈 리스트를 생성하면 앞으로 데이터 타입을 알 수 없기 때문에 값의 타입을 추론할 수 없음 👻 빈 컬렉션의 경우 앞에서처럼 '데이터타입of'만으로는 생성되지 않고 데이터 타입을 직접적으로 알려주는 방법을 사용해야 함
var 변수명 = mutableListOf<컬렉션에 입력될 값의 타입>() var stringList = mutableLisfOf<String>()
// 생성
val stringList = mutableListOf<String>() // 문자열로 된 빈 컬렉션을 생성
stringList.add("월")
stringList.add("화")
println(stringList[1]) // 화
7) 컬렉션 개수 가져오기 : size
👻 size 프로퍼티를 사용하면 컬렉션의 개수를 가져올 수 있음
println("stringList에는 ${stringList.size}개의 값이 있습니다.")
// stringList에는 2개의 값이 있습니다.
예제
문자열을 저장할 수 있는 List 객체를 생성하고 여기에 "a", "b", "c", "d", "e"를 저장한 후 이것을 출력하는 프로그램을 생성 출력 예: [a, b, c, d, e]
자바스크립트를 사용하여 브라우저가 비동기 방식으로 데이터를 요청하고, 서버가 응답한 데이터를 수신하여 웹페이지를 동적으로 갱신하는 프로그래밍 방식. (ex. 구글 지도)
👾 Ajax는 브라우저에서 제공하는 Web API인 XMLHttpRequest 객체를 기반을 동작 👾 XMLHttpRequest : HTTP 비동기 통신을 위한 메서드와 프로퍼티를 제공
👾 이전의 웹페이지는 완전한 HTML을 서버로부터 전송받아 웹페이지 전체를 처음부터 다시 렌더링하는 방식으로 동작 ➡️ 화면이 전환되면 서버로부터 새로운 HTML을 전송받아 웹페이지 전체를 처음부터 다시 렌더링
전통적 방식의 단점
a. 이전 웹페이지와 차이가 없어서 변경할 필요가 없는 부분까지 포함된 완전한 HTML을 서버로부터
매번 다시 전송받기 때문에 불필요한 데이터 통신이 발생 b. 변경할 필요가 없는 부분까지 처음부터 다시 렌더링 한다. 이로 인해 화면 전환이 일어나면 화면이 순간적으로
깜빡이는 현상이 발생한다. c. 클라이언트와 서버와의 통신이 동기 방식으로 동작하기 때문에 서버로부터 응답이 있을 때까지 다음 처리는 블로킹 된다.
Ajax의 장점
a. 변경할 부분을 갱신하는 데 필요한 데이터만 서버로부터 전송받기 때문에 불필요한 데이터 통신 발생 x b. 변경할 필요가 없는 부분은 다시 렌더링하지 않는다. 따라서 화면이 순간적으로 깜빡이는 현상이 발생하지 않는다. c. 클라이언트와 서버와의 통신이 비동기 방식으로 동작하기 때문에 서버에게 요청을 보낸 이후 블로킹이 발생하지 않는다.
Ajax의 단점
a. 즐겨찾기나 검색엔진에 불리 b. 개발이 상대적으로 어려움
💡 '동기(synchronous)'란 - 직렬적으로 태스크를 수행 ▶️ 요청을 보낸 후 응답을 받아야지만 다음 동작이 이루어지는 방식 (순차 진행) 💡 '비동기 (asynchronous)'란 - 병렬적으로 태스크를 수행 ▶️ 요청을 보낸 후 응답의 수락 여부와는 상관없이 다음 태스크가 동작 (멀티 진행)
<script>
document.addEventListener('DOMContentLoaded', function () {
// 1. XMLHttpRequest 객체 생성
const xhr = new XMLHttpRequest();
// 2. HTTP 요청 초기화
xhr.open('GET', 'https://jsonplaceholder.typicode.com/todos/1');
// HTTP 요청 방식(GET, POST), 클라이언트가 HTTP 요청을 보낼 서버의 URL 주소
// 3. 이벤트 등록. XMLHttpRequest 객체의 readyState 프로퍼티 값이 변할 때마다 자동으로 호출
xhr.onreadystatechange = () => {
// readyState 프로퍼티의 값이 DONE : 요청한 데이터의 처리가 완료되어 응답할 준비가 완료됨.
if(xhr.readyState !== XMLHttpRequest.DONE) return;
if(xhr.status === 200) { // 서버(url)에 문서가 존재함
console.log(JSON.parse(xhr.response));
/*
{
"userId": 1,
"id": 1,
"title": "delectus aut autem",
"completed": false
}
*/
const obj = JSON.parse(xhr.response);
console.log(obj.title); // delectus aut autem
} else {
console.error('Error', xhr.status, xhr.statusText);
}
}
xhr.send(); // 4. url에 요청을 보냄.
});
</script>
💡onreadystatechange 서버로 부터 응답이 오게 되어 XMLHttpRequest 객체의 값이 변하게 되면 이를 감지해 자동으로 호출되는 함수를 설정한다. 함수를 등록하게 되면 서버에 요청한 데이터가 존재하고, 서버로부터 응답이 도착하는 순간을 특정할 수 있게 된다.
💡 status 서버의 문서 상태를 표현한다(200 : 존재 / 404 : 미존재)
💡 xhr.send()
send() 메서드를 통해 서버로 객체를 전달한다. 이때 send() 메서드 매개변수를 쓰냐 안쓰냐에 따라 GET / POST 방식으로 다르게 보내게 된다.
💡 readyState 프로퍼티
XMLHttpRequest 객체의 현재 상태를 나타낸다.이 프로퍼티의 값은 객체의 현재 상태에 따라 다음과 같은 주기로 순서대로 변화한다.
fun main() {
val PI = getPi()
println("지름이 10인 원의 둘레는 ${PI}입니다.") // 지름이 10인 원의 둘레는 3.14입니다.
}
2. 함수 파라미터의 정의
🐰 함수에 입력되는 파라미터는 마치 변수를 정의하듯이 '이름 : 타입'의 형태로 정의 ➡️ 여러 개의 파라미터가 정의될 경우는 콤마로 구분 🐰 코틀린에서 함수 파라미터를 통해 입력되는 모든 값은 변하지 않는 immutable 🐰 코틀린에서의 함수 파라미터는 모두 읽기 전용 키워드 val이 생략된 형태
fun 함수이름((val 생략) name1: String, name2: Int, name3: Double) {실행코드}
fun newFun(name: String, age: Int = 29, weight: Double = 65.5) {
// name = "tom"; // Val cannot be reassigned
println("name의 값은 ${name}입니다.")
println("age의 값은 ${age}입니다.")
println("weight의 값은 ${weight}입니다.")
}
// 정의된 newFun() 함수를 호출할 때 기본값이 없는 첫 번째 파라미터에만 값을 입력하면
// 두 번째와 세 번째 파라미터에는 설정한 기본값이 자동으로 입력.
newFun("Hello")
/*
name의 값은 Hello입니다.
age의 값은 29입니다.
weight의 값은 65.5입니다.
*/
2) 파라미터 이름으로 값을 입력하기
🥕 함수에 정의된 파라미터가 많을 경우 입력하는 값의 의미가 명확하지 않을 때가 있음 🥕 이럴 경우 순서와 상관 없이 정의된 파라미터 이름을 지정해서 직접 값을 입력할 수 있음
fun main() {
newFun("Michael", weight=67.5)
/*
name의 값은 Michael입니다.
age의 값은 29입니다.
weight의 값은 67.5입니다.
*/
}
응용 예제
📍login() 메서드와 logout() 메서드를 선언 login() 메서드를 호출할 때는 매개값으로 id와 passwd를 제공하고, logout() 메서드는 id만 매개값으로 제공
그외의 값일 경우에는 false를 리턴 2) logout() 메서드는 "admin 아이디가 로그아웃 되었습니다"가 출력
fun main() {
print("아이디를 입력해주세요. >> ")
val id = readln()
print("비밀번호를 입력해주세요. >> ")
val password = readln()
val result = login(id = id, password = password)
if (result) {
println("로그인 되었습니다.")
logout(id)
} else {
println("id 또는 password가 올바르지 않습니다.")
}
}
fun login(id:String, password: String): Boolean {
return id == "admin" && password == "1234"
}
fun logout(id: String) {
println("$id 아이디가 로그아웃 되었습니다.")
}
📍 배열을 전달 받아 짝수만 출력하는 메서드 작업
fun main() {
val numbers = IntArray(10)
for (i in numbers.indices) {
numbers[i] = i + 1
}
print("main() : ")
for (i in numbers.indices) {
print(numbers[i])
if (i < numbers.size - 1) {
print(", ")
}
} // main() : 1, 2, 3, 4, 5, 6, 7, 8, 9, 10
println()
printOdd(numbers) // printOdd() : 2, 4, 6, 8, 10
}
fun printOdd(numbers: IntArray) {
print("printOdd() : ")
for (i in numbers.indices) {
if (numbers[i] % 2 == 0) {
print(numbers[i])
if ( i < numbers.size - 1) {
print(", ")
}
}
}
val odds = numbers.filter {result -> result % 2 == 0}
println(odds)
}
📍 키보드로 부터 학생 수와 각 학생들의 점수를 입력받아서, 최고 점수 및 평균 점수를 구하는 프로그램
fun main() {
var run = true // 반복문의 조건으로 사용 -> 값이 false가 된다면 반복문이 종료
var scoreList: IntArray = IntArray(0)
while (run) {
println("-----------------------------------------------------")
println("1.학생수 | 2.점수입력 | 3.점수리스트 | 4.분석 | 5.종료")
println("-----------------------------------------------------")
print("선택> ")
val selectNo: Int = readln().trim().toInt()
when (selectNo) {
1 -> scoreList = inputStudentNum() // 학생수를 입력 받아서 배열 생성
2 -> inputScores(scoreList) // 생성된 배열의 갯수 만큼 점수 입력
3 -> printScores(scoreList) // 입력받은 배열의 값을 출력
4 -> printAnalysis(scoreList) // 최고 점수, 평균 점수 출력
5 -> run = setRun() // run 값 변경
}
}
println("프로그램 종료")
}
fun inputStudentNum():IntArray {
print("학생수> ")
var studentNum = readln().toInt()
return IntArray(studentNum)
}
fun inputScores(scores:IntArray) {
for (i in scores.indices) {
print("scores[$i] > ")
scores[i] = readln().toInt()
}
}
fun printScores(scores:IntArray) {
for (i in scores.indices) {
println("scores[$i] : " + scores[i])
}
}
fun printAnalysis(scores: IntArray) {
var max = scores[0]
var sum = scores[0]
var avg: Double
for (i in 1 until scores.size) {
if (scores[i] > max) {
max = scores[i]
}
sum += scores[i]
}
avg = (sum / scores.size).toDouble()
println("최고 점수: " + max)
println("평균 점수: " + avg)
}
fun setRun(): Boolean {
return false
}
🐝 여러 개의 값을 담을 수 있는 대표적인 자료형인 배열은 값을 담기 전에 먼저 배열 공간의 개수를 할당하거나 초기화 시에 데이터를 저장해 두면 데이터의 개수만큼 배열을 크기가 결정 🐝 먼저 개수를 정해 놓고 사용해야 하며 중간에 개수를 추가하거나 제거할 수 없음
💡 배열은 다른 데이터 타입과 마찬가지로 변수로 저장해서 사용할 수 있으며 다음과 같은 형태로 선언 var 변수 = Array(개수)
fun main() {
// 배열 객체는 Int, Long, Char 등과 같은 기본 타입 뒤에 Array를 붙여서 만듦.
var students: IntArray = IntArray(10) // 변수 student에 Int(정수형) 공간을 10개 할당하라는 의미
var longArray = LongArray(10)
var charArray = CharArray(10)
var floatArray = FloatArray(10)
var doubleArray = DoubleArray(10)
println(students[0]) // 0 -> 따로 초기화 하지 않으면 0으로 초기화.
}
1) 문자열 배열에 빈 공간 할당하기
🍯 String은 기본 타입이 아니기 때문에 StringArray는 없지만 다음과 같이 사용할 수 있음
var stringArray = Array(10, {item -> "" })
🍯 괄호 안의 첫 번째 숫자인 10만 변경해서 사용하면 그 숫자만큼 빈 문자열로 된 배열 공간을 할당 ➡️ 자바에서는 문자열 배열을 생성하면서 초기화 하지 않으면 null이 초기값이 됨 ➡️ 하지만 코틀린은 기본적으로 Null 사용을 허용하지 않기 때문에빈 문자열을 이용해서 따로 초기화해야 함
1. 배열을 선언한 변수명 옆에 대괄호([])를 사용하고, 대괄호 안에 값을 저장할 위치의 인덱스 번호를 작성 그리고 등호(=)를 사용해서 값을 입력 배열명[인덱스] = 값
2. set 메서드를 사용할 수 있음. 배열이 제공하는 set()메서드에 인덱스와 값을 파라미터로 넘겨주면 됨 배열명.set(인덱스, 값)
fun main() {
students[0] = 90
students.set(1, 91)
}
4) 배열에 있는 값 꺼내기
🍯 값을 입력할 때와 같은 방식으로 인덱스로 값을 가져올 수 있음
💡 대괄호 안에 인덱스를 입력해서 가져올 수 있으며 값을 꺼내는 배열의 메서드는 get() 배열명[인덱스] 배열명.get(인덱스)
fun main() {
// 배열 students의 일곱번째 값을 seventhValue 변수에 저장
var seventhValue = students[6]
// 배열 students의 열번째 값을 tenthValue 변수에 저장
var tenthValue = students.get(9)
}
응용 예제
fun main() {
/* for문을 이용해서 arr 배열의 합을 구하세요 */
val arr = intArrayOf(10, 20, 30, 40, 50)
var sum = 0
// 작성 위치
// 1. until 사용
for (i in 0 until arr.size) {
sum += arr[i]
}
println("arr 배열의 합은 $sum")
// 2. for in 사용
sum = 0
for (item in arr) {
sum += item
}
println("arr 배열의 합은 $sum")
// 3. indices 사용
println(arr.indices) // 0..4
sum = 0
for (i in arr.indices) {
sum += arr[i]
}
println("sum = $sum")
}