일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
- 자취필수템
- 프래그먼트
- 목적중심리더십
- 재택근무
- 함수형 프로그래밍
- 캐치마인드
- 북한살둘레길
- 브런치작가되기
- T자형인재
- 리얼하다
- 한단어의힘
- 안드로이드
- 한달브런치북만들기
- 베드테이블
- 소프시스
- 한달독서
- 아비투스
- 목적 중심 리더십
- 소프시스 밤부 좌식 엑슬 테이블
- 끝말잇기
- 베드트레이
- 테트리스
- 어떻게 나답게 살 것인가
- 커스텀린트
- 지지않는다는말
- 좌식테이블
- 한달어스
- 1일1커밋
- 슬기로운 온라인 게임
- 면접
- Today
- Total
정상에서 IT를 외치다
[스트림 연습] Kotlin Collection Example 본문
안녕하세요. 블랙진입니다.
스트림 연습 포스팅입니다.
변환(map, groupby)
Map
public inline fun <T, R> Iterable<T>.map(transform: (T) -> R): List<R> {
return mapTo(ArrayList<R>(collectionSizeOrDefault(10)), transform)
}
T타입의 이터레이터를 받아 R타입의 리스트로 반한한다.
val cities = listOf("Seoul","Tokyo","MountainView")
// 1. map에는 함수를 넣을 수 있다. 함수는 { } 안에 -> 를 통해 왼쪽과 오른쪽 인자로 구분된다.
cities.map({ str:String -> str.toUpperCase() }).forEach { print(it) }
// 2. ()안에 인자로 함수 1개만 있는 경우 { } 는 밖으로 나올 수 있다.
cities.map() { str:String -> str.toUpperCase() }.forEach { print(it) }
// 3. (){ }에서 ()는 생략할 수 있다.
cities.map { str:String -> str.toUpperCase() }.forEach { print(it) }
// 4. 스트림 함수에서 기본적으로 반환되는 인자는 it이다.
cities.map { it:String -> it.toUpperCase() }.forEach { print(it) }
// 5. it 인자 1개만 있는 경우 생략이 가능하다
cities.map { it.toUpperCase() }.forEach { print(it) }
// 6. 위와 같은 경우 메소드 레퍼런스로도 표현이 가능하다.
cities.map(String::toUpperCase).forEach { print(it) }
Groupby
public inline fun <T, K> Iterable<T>.groupBy(keySelector: (T) -> K): Map<K, List<T>> {
return groupByTo(LinkedHashMap<K, MutableList<T>>(), keySelector)
}
T타입의 이터레이터를 받아 K타입의 키와 T타입 리스트의 값으로 변환한다.
val keySelector = { city: String -> if(city.length > 5) "A" else "B"}
// 함수를 인자로 넘겨줍니다.
cities.groupBy(keySelector)
.forEach { (key, cities) ->
println("$key : $cities")
}
// 위 생략 과정처럼 아래와 같이 표현할 수 있습니다.
cities.groupBy { if(it.length > 5) "A" else "B" }
.forEach { (key, cities) ->
println("$key : $cities")
}
forEach에서 구문분해 구문을 사용해 for을 표현할 수 있습니다.
필터(filter, take, drop, first, last, distinct)
filter
public inline fun <T> Iterable<T>.filter(predicate: (T) -> Boolean): List<T> {
return filterTo(ArrayList<T>(), predicate)
}
T타입의 이터레이터를 받아 조건에 부합한 값만 다시 T타입의 리스트로 만들어 줍니다.
cities.filter { it.length <= 5 }.forEach { println(it) }
take
public fun <T> Iterable<T>.take(n: Int): List<T> {
require(n >= 0) { "Requested element count $n is less than zero." }
if (n == 0) return emptyList()
if (this is Collection<T>) {
if (n >= size) return toList()
if (n == 1) return listOf(first())
}
var count = 0
val list = ArrayList<T>(n)
for (item in this) {
if (count++ == n)
break
list.add(item)
}
return list.optimizeReadOnlyList()
}
1. take 인자가 0 이상인지 체크합니다.
2. 수신객체가 Collection 인지 확인합니다.
3. 수신객체가 비어있는지 확인합니다.
4. 새로운 ArrayList를 만든 후 count가 n이 될 때까지 add를 해줍니다.
cities.take(1).forEach { println(it) }
cities.takeLast(1).forEach { println(it) }
//첫 인자에서부터 해당 조건을 만족 할 때 까지 배출
cities.takeWhile { it.length > 5 }.forEach { println(it) }
drop
public fun <T> Iterable<T>.drop(n: Int): List<T> {
require(n >= 0) { "Requested element count $n is less than zero." }
if (n == 0) return toList()
val list: ArrayList<T>
if (this is Collection<*>) {
val resultSize = size - n
if (resultSize <= 0)
return emptyList()
if (resultSize == 1)
return listOf(last())
list = ArrayList<T>(resultSize)
if (this is List<T>) {
if (this is RandomAccess) {
for (index in n until size)
list.add(this[index])
} else {
for (item in listIterator(n))
list.add(item)
}
return list
}
}
else {
list = ArrayList<T>()
}
var count = 0
for (item in this) {
if (count++ >= n) list.add(item)
}
return list.optimizeReadOnlyList()
}
1. drop 인자가 0 이상인지 체크합니다.
2. 수신객체가 Collection 인지 확인합니다.
3. 수신객체가 비어있는지 확인합니다.
4. 수신객체 크기에서 n을 제외한 사이즈의 ArrayList를 생성합니다.
5. n 부터 수신객체 크기까지의 데이터만 add를 해줍니다.
cities.drop(1).forEach { println(it) }
//첫 인자에서부터 해당 조건을 만족 할 때 까지 제외
cities.dropWhile { it.length > 5 }.forEach { println(it) }
first
public fun <T> List<T>.first(): T {
if (isEmpty())
throw NoSuchElementException("List is empty.")
return this[0]
}
수신객체가 비어 있지 않으면 첫 번째 인자만 반환합니다.
println(cities.first())
public inline fun <T> Iterable<T>.first(predicate: (T) -> Boolean): T {
for (element in this) if (predicate(element)) return element
throw NoSuchElementException("Collection contains no element matching the predicate.")
}
조건을 만족하는 첫 번째 인자만 반환합니다. 조건에 만족하는 값이 없으면 예외를 던집니다.
println(cities.first { it.length > 5 })
last
public fun <T> List<T>.last(): T {
if (isEmpty())
throw NoSuchElementException("List is empty.")
return this[lastIndex]
}
수신객체가 비어 있지 않으면 마지막 인자만 반환합니다.
println(cities.last())
public inline fun <T> Iterable<T>.last(predicate: (T) -> Boolean): T {
var last: T? = null
var found = false
for (element in this) {
if (predicate(element)) {
last = element
found = true
}
}
if (!found) throw NoSuchElementException("Collection contains no element matching the predicate.")
@Suppress("UNCHECKED_CAST")
return last as T
}
조건을 만족하는 마지막 인자만 반환합니다. 조건에 만족하는 값이 없으면 예외를 던집니다.
distinct
public fun <T> Iterable<T>.distinct(): List<T> {
return this.toMutableSet().toList()
}
수신객체를 MutableSet으로 변환 후 다시 리스트로 바꿔 줍니다. 참고로 list를 set으로 변환하면 중복데이터는 제거됩니다.
val citiesForDistinct = listOf("Seoul","Tokyo","Mountain View","Seoul","Tokyo")
citiesForDistinct.distinct().forEach { println(it) }
distinctBy
public inline fun <T, K> Iterable<T>.distinctBy(selector: (T) -> K): List<T> {
val set = HashSet<K>()
val list = ArrayList<T>()
for (e in this) {
val key = selector(e)
if (set.add(key))
list.add(e)
}
return list
}
1. 수신객체의 각 아이템에 selector값을 key로 뽑습니다.
2. HashSet에 key를 add 해주는데 중복되어 있는 값이면 false를 반환합니다.
3. HashSet에 add 했을 때 true를 반환하는 값만 list에 추가 후 반환합니다.
val citiesForDistinct = listOf("Seoul","Tokyo","Mountain View","Seoul","Tokyo")
//해당 조건에 맞는 중복값을 제거합니다.
citiesForDistinct.distinctBy { it.length }.forEach { println(it) }
결과 [ Seoul, Mountain View]
조합 및 합계(zip, joinToString(), count, reduce, fold)
zip
public infix fun <T, R> Iterable<T>.zip(other: Iterable<R>): List<Pair<T, R>> {
return zip(other) { t1, t2 -> t1 to t2 }
}
두 이터레이터를 받아 Pair<T, R>을 List로 묶어 반환해 줍니다.
public infix fun <A, B> A.to(that: B): Pair<A, B> = Pair(this, that)
{ t1, t2 -> t1 to t2 } 에서 to는 infix 함수로 Pair(t1, t2)를 보기 쉽게 t1 to t2 로 바꿔 주는 기능을 합니다. 이 둘은 문법만 다를 뿐 똑같습니다.
val cityCodes = listOf("SEO", "TOK", "MTV", "NYC")
val cityNames = listOf("Seoul","Tokyo","MountainView")
cityCodes.zip(cityNames)
.forEach {
println(it.first + " : " + it.second)
}
결과
- SEO : Seoul
- TOK : Tokyo
- MTV : MountainView
joinToString
public fun <T> Iterable<T>.joinToString(separator: CharSequence = ", ", prefix: CharSequence = "", postfix: CharSequence = "", limit: Int = -1, truncated: CharSequence = "...", transform: ((T) -> CharSequence)? = null): String {
return joinTo(StringBuilder(), separator, prefix, postfix, limit, truncated, transform).toString()
}
수신객체의 값을 separator로 구분하여 하나의 String 으로 만들어 줍니다.
println(cities.joinToString())
println(cities.joinToString(separator = "|"))
결과
Seoul, Tokyo, MountainView
Seoul|Tokyo|MountainView
count
public inline fun <T> Collection<T>.count(): Int {
return size
}
수신객체의 사이즈를 반환합니다.
public inline fun <T> Iterable<T>.count(predicate: (T) -> Boolean): Int {
if (this is Collection && isEmpty()) return 0
var count = 0
for (element in this) if (predicate(element)) checkCountOverflow(++count)
return count
}
조건을 추가하여 조건에 맞는 값의 갯수만 반환합니다.
println(cities.count { it.length <= 5 })
결과 [2]
reduce
public inline fun <S, T : S> Iterable<T>.reduce(operation: (acc: S, T) -> S): S {
val iterator = this.iterator()
if (!iterator.hasNext()) throw UnsupportedOperationException("Empty collection can't be reduced.")
var accumulator: S = iterator.next()
while (iterator.hasNext()) {
accumulator = operation(accumulator, iterator.next())
}
return accumulator
}
1. 수신객체를 이터레이터로 만듭니다.
2. 이터레이터가 비어 있는지 확인 후 비어 있다면 예외를 던집니다.
3. 이터레이터의 첫 번째 인자를 acculator에 넣습니다.
4. 이터레이터에 다음 값이 있다면 operation 함수에 acculator와 다음 값을 넣고 이 둘이 조합된 값을 다시 acculator에 넣어줍니다.
println(cities.reduce { acc, s -> acc })
println(cities.reduce { acc, s -> "$acc $s" })
결과
Seoul
Seoul Tokyo MountainView
fold
public inline fun <T, R> Iterable<T>.fold(initial: R, operation: (acc: R, T) -> R): R {
var accumulator = initial
for (element in this) accumulator = operation(accumulator, element)
return accumulator
}
reduce와 비교해서 비어있는지 여부를 체크하지 않아도 됩니다. reduce와 동작은 같으나 fold에서는 초기값을 받아 acculator에 설정해 주기 때문입니다.
println(cities.fold("BlackJIn") { acc, s -> "$acc $s" })
결과 [BlackJIn Seoul Tokyo MountainView]
기타(any, none, max, min, average)
any
public inline fun <T> Iterable<T>.any(predicate: (T) -> Boolean): Boolean {
if (this is Collection && isEmpty()) return false
for (element in this) if (predicate(element)) return true
return false
}
수신객체에 predicate 조건에 부합하는 값이 1개라도 있으면 true을 반환합니다.
println(cities.any { it.length <= 5 })
none
public inline fun <T> Iterable<T>.none(predicate: (T) -> Boolean): Boolean {
if (this is Collection && isEmpty()) return true
for (element in this) if (predicate(element)) return false
return true
}
수신객체에 predicate 조건에 부합하는 값이 1개라도 있으면 false을 반환합니다.
println(cities.none { it.isEmpty() })
max
public fun <T : Comparable<T>> Iterable<T>.max(): T? {
val iterator = iterator()
if (!iterator.hasNext()) return null
var max = iterator.next()
while (iterator.hasNext()) {
val e = iterator.next()
if (max < e) max = e
}
return max
}
가장 큰값 반환
min
public fun <T : Comparable<T>> Iterable<T>.min(): T? {
val iterator = iterator()
if (!iterator.hasNext()) return null
var min = iterator.next()
while (iterator.hasNext()) {
val e = iterator.next()
if (min > e) min = e
}
return min
}
가장 작은값 반환
average
public fun Iterable<Int>.average(): Double {
var sum: Double = 0.0
var count: Int = 0
for (element in this) {
sum += element
checkCountOverflow(++count)
}
return if (count == 0) Double.NaN else sum / count
}
평균 값 반환
val numbers = listOf(4,6,8,21,9,10,2,0)
println(numbers.max())
println(numbers.min())
println(numbers.average())
결과
max : 21
min : 0
average : 7.5
'안드로이드' 카테고리의 다른 글
[리엑티브 연습] AsyncSubject, BehaviorSubject, PublishSubject, ReplaySubject (0) | 2019.06.25 |
---|---|
[리엑티브 연습] Observable, Single, Maybe, Completable (0) | 2019.06.25 |
[리엑티브 연습] merage, debounce (로그인 예제) (0) | 2019.06.24 |
[in-app updates] 안드로이드 인 앱 업데이트 사용기 (21) | 2019.06.21 |
[Firebase Crashlytics] 안드로이드 에러 리포트 사용기 (3) | 2019.06.20 |