>알고리즘 문제
1. 문제
문자열 s에는 공백으로 구분된 숫자들이 저장되어 있습니다. str에 나타나는 숫자 중 최소값과 최대값을 찾아 이를 "(최소값) (최대값)"형태의 문자열을 반환하는 함수, solution을 완성하세요.
예를들어 s가 "1 2 3 4"라면 "1 4"를 리턴하고, "-1 -2 -3 -4"라면 "-4 -1"을 리턴하면 됩니다.
제한 조건
- s에는 둘 이상의 정수가 공백으로 구분되어 있습니다.
2. 솔루션
class Solution {
fun solution(s: String): String {
var list=s.split(" ")
var arr=arrayOf<Int>()
list.forEach{arr+=it.toInt()}
return "${arr.minOrNull()} ${arr.maxOrNull()}"
}
}
-maxOrNUll, minOrNull : min,max가 deprecated되었기에 이걸사용. 최대 최소나 null을 반환해줌
@sorted: 리스트에 정렬 사용시 이걸 사용
>코드카타 리뷰
1. iterator
-사용법: iterator를 선언후 while문에서 hasNext()로 다음 원소가 있는 한 순회를 하며 next()를 이요
val iterator = 컬렉션.iterator()
while(iterator.hasNext()){
val item=iterator.next()
.
.
}
2. map
-원본과 원소개수는 같지만 람다식에 의해 원소가 변형된 새로운 컬렉션을 만드는 함수
- 원본컬렉션.map{ it으로 구성된 함수 }
>코틀린 문법 강의 5주차
1. 자료형 변환
1)일반 자료형간의 변환: to자료형()
2)객체 자료형간의 변환
<1>상속 관계 자료형간만이 가능
<2>종류
-업캐스팅: 자식클래스를 부모클래스로 변환
=>예시
fun main() {
var birds = mutableListOf<Bird>()
birds.add(Sparrow(name) as Bird) //부모클래스 컬렉션에 자식클래스 객체 추가. as Bird는 생략가능
}
open class Bird(name: String) {
var name: String
init {
this.name = name
}
}
class Sparrow(name: String): Bird(name) {
}
@주생성자와 프로퍼티의 이름이 같을시 위와같이 init에서 초기화하면 됨
-다운캐스팅: 업캐스팅된 자료형을 원래대로 변환시키는 것. 마찬가지로 자식클래스를 자료형으로 가지는 변수나 컬렉션 같은곳에 다시 넣으면 된다.
@업캐스팅을 되돌리는 것만 가능하고 원래부터 부모클래스였던 인스턴스는 다운캐스팅 불가!
=>업캐스팅한 객체를 다운캐스팅 가능한 이유: 업캐스팅해도 메모리상에 업캐스팅전 원본 정보가 남아있기때문이다.
2. 여러 인스턴스 리턴: 메소드가 여러 데이터 리턴하게 해주는 방법
1)다루는 법: 결과값을 toList()로 리스트화 시켜서 다루면 된다.
2)종류
<1>Pair
-메소드 반환 자료형:Pair<자료형1, 자료형2>
-사용:Pair(결과1, 결과2)
<2>Triple
-메소드 반환 자료형:Triple<자료형1, 자료형2, 자료형3>
-사용:Triple(결과1, 결과2, 결과3)
3. Scope Function
1)사용: 자기자신의 객체를 전달하여 효율적으로 처리 가능
2)종류
<1>let
-사용: .?let{ 앞의 객체를 it으로 받음. 수행코드 }
-결과: 블록 내 수행결과를 반환. 객체가 null인 경우 null반환
-예시
var result = strNum?.let {
Integer.parseInt(it)
}
<2>with
-사용: with(객체){ 앞의 객체를 this로 받고 생략 가능. 수행코드 }
-결과: 블록 내 코드 수행. 객체는 반드시 null이 아니어야 함
@세이프콜(.?)을 지원하지 않기에 let과 병합해서 쓰기도 함. =>?.let{ with(it){ ~ } }
-예시
with(alphabets) {
//var result = this.subSequence(0,2)
var result = subSequence(0,2)
println(result)
}
<3>also
-사용: .?also{ 앞의 객체를 it으로 받음. 수행코드}
-결과: 블록내 수행결과와 상관없이 앞에서 받았던 객체를 반환=>즉, 주로 객체를 수정할때 사용
-예시
var result = student?.also {
it.age = 50
}
<4>apply
-사용: ?.apply{ 앞의 객체를 this로 받고 생략 가능. 수행코드 }
-결과: 블록내 수행결과와 상관없이 앞에서 받았던 객체를 반환=>즉, 주로 객체를 수정할때 사용
-예시
var result = student?.apply {
age = 50
}
<5>run
-사용:
=>객체에서 호출하는 경우: ?.run{ 앞의 객체를 this로 받고 생략 가능. 수행코드 }
=>객체에서 호출하지 않는 경우: run{ 수행코드 }
-결과: 블록 내 수행 결과 반환
@null체크가 가능하므로 with보다 안전하게 사용가능
3)유사성과 차이점
<1>run 과 let: run은 주로 반환값을 얻기위해 사용되며 let은 주로 어떤 작업을 하기 위해 사용됨
-예시
val result = "hello".run {
println("The length of this string is $length")
length // 반환 값
}
str?.let {
println("The length of the string is ${it.length}")
}
<2>also와 apply: 둘 다 수신객체를 반환하는 함수이나 also는 수신객체로 it을 받고 apply는 수신객체로 this를 받음
<3>run과 with: 둘다 비슷하게 사용되나 run이 null체크가 가능하므로 더 안전
@표
Scope에서 접근방식 this | Scope에서 접근방식 it | |
블록 수행 결과를 반환 | run, with | let |
객체 자신을 반환 | apply | also |
4)Scope function이 중복되어 사용되는 경우:it이 여러번 나타나므로 람다식을 {c->it대신 c사용} 꼴로 바꾸어 줌
-예시
Person().also {
it.name = "한석봉"
it.age = 40
val child = Person().also { c ->
c.name = "홍길동"
c.age = 10
}
it.child = child
}
@this가 사용되는 것은 람다식으로 바꾸기 불가능
4. 확장함수
1)사용: 클래스에 메소드를 외부에서 추가하고 싶을때 사용
2)주의점:
-확장함수는 클래스의 public 멤버에만 접근할 수 있음
-하위 클래스에서 확장함수는 상속 불가능=>즉, 오버라이드도 불가능함.
3)예시
fun main() {
//확장함수
fun Student.getGrade() = println("학생의 등급은 ${this.grade} 입니다")
var student = Student("참새", 10, "A+")
student.getGrade()
}
class Student(name: String, age: Int, grade: String) {
var name: String
var age: Int
var grade: String
init {
this.name = name
this.age = age
this.grade = grade
}
}
5. 비동기 프로그래밍
1) 의미
-동기 프로그래밍: 순차적으로 하나씩 작업을 수행. 요청을 보내고 결과값 반환까지 작업을 멈춤
-비동기 프로그래밍: 여러 일을 한번에 수행. 요청을 보내고 결과값을 받는 동안 멈추지 않고 또 다른 일을 수행
=>context switching이 중요!: 한 cpu에서 여러 작업을 수행하기 위해 스케줄링 하는 것
6. 쓰레드와 코루틴
1)사전작업: 외부 종속성 추가 필요
dependencies {
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.9.0-RC")
}
2)쓰레드
<1>의미
-프로세스: 프로그램이 메모리에 올라가서 실행되면 이걸 프로세스라고 함.
-쓰레드: 프로세스보다 작은 단위. 쓰레드는 독립된 메모리 영역을 가지고 이 영역은 스택형태로 되어있음.
=>쓰레드 생성시 스택메모리 일정 영역 차지
<2>사용: thread(start = true){}로 새 쓰레드 생성하여 사용 가능
<3>예시
fun main() {
thread(start = true) {
for(i in 1..10) {
println("Thread1: 현재 숫자는 ${i}")
runBlocking {
launch {
delay(1000)
}
}
}
}
}
-delay(num): num에 해당하는 ms(마이크로 세컨드. 1000분의 1초)동안 일시 정지
-thread import시 더 긴걸로
-delay import시 Long으로
<4>@Volatile
-사용: 일반적인 경우 변수에 대한 쓰기작업은 CPU캐시에 저장 =>다른 쓰레드에서 변경된값 읽기 불가
=>Volatile사용시 변수값을 즉시 메모리에 반영하므로 다른 쓰레드에서도 읽기 가능
-예시
class SharedData {
@Volatile
var sharedVariable: Int = 0
}
=>sharedVariable이란 변수를 @Volatile이란 키워드를 붙여 선언: 값이 변경되면 즉시 메모리에 반영
3)코루틴
<1>의미: 비동기 프로그래밍을 위한 경량 쓰레드 =>코드 조작을 실행하는데 사용
-경량 쓰레드: 쓰레드 보다 가볍고 쓰레드 풀 없이도 수많은 동시성 작업 수행 가능하게 해줌.
-비동기적 실행
-일시 중단 및 재개: 코루딘은 중단 및 재개가 가능하여 비동기 작업을 동기적으로 보이게 하고 코드 가독성과 유지 보수성 향상
-구조적 동시성: 코드를 동시성 작업으로 분할 할 수 있도록 구조를 제공
@구조적 동시성: 동시성 작업을 관리하기 위한 패러다임
-Scope 관리: 동시성 작업이 특정 스코프 내에 존재 =>메모리 누수 방지, 작업 생명주기의 명확한 관리
-부모 - 자식 관계: 동시성 작업이 부모 - 자식 관계 형성 =>부모작업 완료시 자식 작업도 함꼐 중단 혹은 완료됨
-예외 처리: 부모작업에서 발생한 예외를 모든 자식 작업에게 전파=>예외 처리를 명확히 해 예외 처리가 안됐을때 어플리케이션이 전체 중단되는 것을 방지
@동시성 작업: 동시에 여러 작업이 실행되는 것
-병렬성: 동시에 여러 작업이 실행되는것
-비동기성: 여러작업이 동시에 실행되나 서로 독립적인것.
-이벤트 기반 작업: 이벤트 루프를 통해 비동기적으로 실행. 외부 이벤트나 사용자입력 같은 것을 처리하는 데 사용
@이벤트 루프: 비동기적 이벤트를 처리하고 응용 프로그램이 반응적 동작 수행을 도와주는 프로그래밍 패턴
=>수행작업들:
- 이벤트 대기: 여러 소스로부터 이벤트 대기. 예를 들어 시스템 호출, 네트워크 소켓, 사용자 입력등
- 이벤트 처리: 이벤트 발생시 처리. 새로운 작업을 시작하거나 이벤트에 따른 동작수행등을 의미
-비동기 작업 관리: 비동기 작업 완료를 기다리거나 작업완료시 이벤트 발생시킴
-이벤트 큐 처리: 이벤트 루프는 대기중 이벤트 처리를 위해 이벤트 큐 사용. 이벤트가 큐에 추가되고 처리시까지 이벤트 루프 대기
<2>코루틴 빌더
[1]lauch: 가장 간단한 코루틴 빌더
-코루틴 생성 후 결과로 Job 객체 반환
-반환된 Job객체로 코루틴 제어 가능
-launch로 생성된 코루틴은 결과를 반환하지 않음 =>작업 완료 여부나 결과를 기다릴 필요 없을 경우 사용
@Job객체 함수
-join(): 코루틴이 종료되기를 기다림
-cancle(): 코루틴을 즉시 종료
-isActive(): 코루틴이 실행중인지 여부 확인
-예시
import kotlinx.coroutines.*
fun main() {
// Job 객체 생성
val job = GlobalScope.launch {
delay(1000L)
println("World")
}
println("Hello")
// 코루틴이 완료될 때까지 기다림
runBlocking {
job.join()
println("Done")
}
}
-원래라면 launch로 생성된 코루틴내 println이 실행되기전에 메인쓰레드가 종료되어 끝나지만 job.join()으로 job이 끝나기를 기다림.
-runBlocking은 블록내의 코드가 순차 실행되므로 job에 해당하는 코루틴이 완료된다음 println이 실행된다
[2]async
-코루틴 생성후 결과로 Deferred객체 반환
-Deferred는 코루틴 실행 결과를 나타내며 await()함수로 해당 결과를 기다려 얻
-async로 생성된 코루틴은 결과를 반환함 => 다른 코루틴에서 작업 수행 후 해당 작업 결과를 기다려야 하는 경우 사용됨
[3]예시
fun main() {
// launch를 사용하여 코루틴 생성
val job = GlobalScope.launch {
delay(1000L)
println("World")
}
println("Hello")
// async를 사용하여 코루틴 생성
val deferred = GlobalScope.async {
delay(1000L)
"World"
}
println("Hello")
// async의 결과를 기다림
runBlocking {
val result = deferred.await()
println(result)
}
// launch의 작업을 취소
job.cancel()
}
@runBlocking{}
-비동기적 실행 작업으로 코드내용이 실행 완료될때까지 코루틴이 완료되지 않게 막음
-주로 테스트에 사용
-블록 내의 코드는 순차적(동기적)으로 실행됨
-예시
fun main() {
runBlocking {
// 코루틴 내에서 비동기 작업 시작
val job = launch {
delay(1000) // 1초 동안 대기
println("Coroutine completed")
}
println("Before coroutine")
job.join() // 코루틴이 완료될 때까지 대기
println("After coroutine")
}
}
//before => coroutine => after순으로 출력
<3>코루틴 스코프
-코루틴 범위 지정
-종류
[1]GlobalScope: 앱이 실행된 이후 계속 수행
[2]CoroutineScope: 필요할때 생성하고 사용 후 정리 필요
=>cancle()을 이용해 정리함
-Dispatcher
[1]의미: 코루틴을 실행할 쓰레드를 지정
[2]종류
-Dispatchers.Main: UI와 상호작용하기 위한 메인쓰레드
-Dispatchers.IO: 네트워크나 디스크 I/O작업에 최적화되어있는 쓰레드
-Dispatchers.Default: 기본적으로 CPU최적화되어있는 쓰레드
-예시
println("메인쓰레드 시작")
var job = CoroutineScope(Dispatchers.Default).launch {
var fileDownloadCoroutine = async(Dispatchers.IO) {
delay(10000)
"파일 다운로드 완료"
}
var databaseConnectCoroutine = async(Dispatchers.IO) {
delay(5000)
"데이터베이스 연결 완료"
}
println("${fileDownloadCoroutine.await()}")
println("${databaseConnectCoroutine.await()}")
}
runBlocking {
job.join()
}
println("메인쓰레드 종료")
job.cancel()
-파일 다운과 데이터베이스 연결을 병렬처리하여 메인쓰레드가 블로킹 되지 않도록 함.
-await()로 작업이 완료되기를 기다리고 이후 결과 출력하게해 코루틴간 의존성 관리
4)쓰레드와 코루틴 차이
<1>쓰레드
-작업단위: Thread
=>각 쓰레드가 독립적인 stack메모리 영역을 가짐
-동시성 보장 수단: Context switching
[1]운영체제 커널에 의한 context switching으로 동시성 보장
[2]블로킹(blocking): 쓰레드 A가 쓰레드 B의 결과를 기다리고 있으면 쓰레드 A를 블로킹 상태라 한다
=>A는 B의 결과가 나올떄까지 해당 자원 사용 불가
<2>코루틴
-작업단위: Coroutine Object
=>여러 작업에 각각 Object할당
=>이 역시 객체이기에 JVM Heap에 적재
@Heap: 동적 메모리 저장소. 동적으로 변하는 데이터 구조 저장에 사용
-동시성 보장 수단: Programmer Switching
[1]소스 코드를 통해 Switching시점을 정함 =>OS는 관여X
[2]Suspend<유예>(Non-Blocking):
-Object1이 Object2의 결과를 기다릴때 1은 suspend로 바뀜
-1을 수행하던 쓰레드는 유효
-즉, 2도 1과 동일 쓰레드에서 진행
-여러 task를 하나의 쓰레드에서 수행가능
'코틀린-안드로이드' 카테고리의 다른 글
18일차)알고리즘 문제(이진 변환 반복하기) (1) | 2024.06.09 |
---|---|
17일차)알고리즘 문제(JadenCase 문자열 만들기) (0) | 2024.06.08 |
15일차)알고리즘 문제(신고 결과 받기) (1) | 2024.06.06 |
14일차)알고리즘 문제(공원 산책), 코드카타 리뷰(indices) (1) | 2024.06.05 |
13일차)알고리즘 문제(달리기 경주), 코트카타 리뷰, 코틀린 문법 강의 4주차(접근제한자, 예외 처리, 지연초기화, 널 세이프티, 배열, 컬렉션, 싱클) (1) | 2024.06.04 |