반응형
Notice
Recent Posts
Recent Comments
Link
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 | 31 |
Tags
- Effective Java 3
- Effective Java
- Sort
- 스프링
- 김영한
- 카카오 면접
- 스프링부트
- JavaScript
- 자바
- 클린아키텍처
- 이차전지관련주
- 자바스크립트
- effectivejava
- ElasticSearch
- java
- 스프링 핵심원리
- 코딩테스트
- 예제로 배우는 스프링 입문
- 카카오
- 이펙티브 자바
- 오블완
- Spring
- 엘라스틱서치
- 알고리즘
- 스프링핵심원리
- k8s
- 이펙티브자바
- 티스토리챌린지
- 알고리즘정렬
- kubernetes
Archives
- Today
- Total
Kim-Baek 개발자 이야기
자바에서 코틀린 - 6장 자바에서 코틀린 컬렉션으로 본문
자바의 컬렉션은 가변이다. 이 때문에 발생하는 문제를 먼저 살펴본다
6.1
package travelator;
public class Suffering {
public static int sufferScoreFor(List<Journey> route) {
Location start = getDepartsFrom(route);
List<Journey> longestJourneys = longestJourneysIn(route, 3);
return sufferScore(longestJourneys, start);
}
}
- start 가 별게 없어서 인라인 한다. (6.2)
6.2
public static int sufferScoreFor(List<Journey> route) {
List<Journey> longestJourneys = longestJourneysIn(route, 3);
return sufferScore(longestJourneys, getDepartsFrom(route));
}
- 이렇게 하고 난 뒤 문제가 발생한다.
6.3
package travelator;
import java.util.List;
public class Routes {
public static Location getDepartsFrom(List<Journey> route) {
return route.get(0).getDepartsFrom();
}
}
- 위처럼 수정한 후, 출발 로케이션을 찾는 곳에서 버그 발생
6.4
public static List<Journey> longestJourneysIn(
List<Journey> journeys,
int limit
) {
journeys.sort(comparing(Journey::getDuration).reversed()); // <1>
var actualLimit = Math.min(journeys.size(), limit);
return journeys.subList(0, actualLimit);
}
- longestJourneysIn 에서 Sort 가 순서를 바꿔버렸다.
- Collections.unmodifiableList() 를 썼으면?
- 실행 시점에 확인이 가능
- UnsupportedOperationException 에러가 났을 것이다.
- 래핑을 하는 것이기 때문에 원본리스트를 변경할 수 있다면 역시나 문제가 생길 수 있음.
- 실행 시점에 확인이 가능
공유된 컬렉션을 변경하지 말라
- 다른 코드에서 공유되는 컬렉션이 있으면 불변 컬렉션으로 취급하라
- 불변이 아닐지라도 이 원칙을 적용해라
- 생성하되 변경하지 말라. ( 전략 )
- 자바 컬렉션
- 가변
- 스칼라
- 불변 컬렉션 ( 자바와 쓰기 위해서 컬렉션 복사 필요 )
- 코틀린
- 자바의 컬렉션 인터페이스에서 상태를 변경하는 메소드 제거
- List<E> , Collection<E>
- 상태 변경을 위해서, 위의 인터페이스를 확장한 MutableCollection<E>, MutableList<E>
- 자바의 컬렉션 인터페이스에서 상태를 변경하는 메소드 제거
- 코틀린의 불변 List / MutableList 에 자바의 List 를 대입해 사용된다.
- 문제는 불변 값인 List에 가변 리스트가 들어가 값이 변경될 수 있다는 것.
val aMutableList = mutableListOf("0","1") val aList: List<String> = aMutableList //코틀린에서 List 는 불변 aMutableList[0] = "123123" assertEquals("0", aList[0] )
- 이걸 제대로 해결하려면 불변과 가변의 하위 타입 관계를 없애는 것이다.
- 대신 StringBuilder / String 처럼 매번 복사를 해야한다.
- 그런데 왜 코틀린은 이렇게 안했을까?
- “공유된 컬렉션을 변경하지 말라” → 이걸 지킨다면 안전할 것이라 생각
- 자바와의 상호 운영성을 가져가기 위해서.
inline fun <T, R> Iterable<T>.map(transform: (T) -> R): List<R> { val result = ArrayList<R>() for (item in this) result.add(transform(item)) return result }
- 코틀린 표준 라이브러리도 그런 식으로 설계가 되어 있다.
- 자바의 sort는 복사본을 정렬 후, 원본을 복사본에 일치시킨다. ( 가변 )
- 이걸 바꾸자
- package travelator; public class Collections { @SuppressWarnings("unchecked") public static <E> List<E> sorted( Collection<E> collection, Comparator<? super E> by ) { var result = (E[]) collection.toArray(); // 새로운 어레이 리턴 Arrays.sort(result, by); return Arrays.asList(result); // Array. 를 리턴하는데 원소 추가삭제 불가 } }
•The returned array will be "safe" in that no references to it are maintained by this list. (In other words, this method must allocate a new array). The caller is thus free to modify the returned array.
6.7
static List<Journey> longestJourneysIn(
List<Journey> journeys,
int limit
) {
var actualLimit = Math.min(journeys.size(), limit);
return sorted(
journeys,
comparing(Journey::getDuration).reversed()
).subList(0, actualLimit);
}
- 우리가 새로 만든 sorted 사용
- 불변!
6.9
public static int sufferScoreFor(List<Journey> route) {
Location start = getDepartsFrom(route);
List<Journey> longestJourneys = longestJourneysIn(route, 3);
return sufferScore(longestJourneys, start);
}
==========================================================
public static int sufferScoreFor(List<Journey> route) {
return sufferScore(
longestJourneysIn(route, 3),
getDepartsFrom(route));
}
- 이제 값이 불변이니 다시, 인라이닝 한다.
6.10
public static List<List<Journey>> routesToShowFor(String itineraryId) {
var routes = routesFor(itineraryId);
removeUnbearableRoutes(routes);
return routes;
}
private static void removeUnbearableRoutes(List<List<Journey>> routes) {
routes.removeIf(route -> sufferScoreFor(route) > 10);
}
- removeUnbearableRoutes 는 void 를 리턴하면서, 내부에서 파라미터를 변경해버리는 구나… 를 알게 해준다.
private static List<List<Journey>> removeUnbearableRoutes
(List<List<Journey>> routes
) {
routes.removeIf(route -> sufferScoreFor(route) > 10);
return routes;
}
=============================================
private static List<List<Journey>> bearable
(List<List<Journey>> routes
) {
return routes.stream()
.filter(route -> sufferScoreFor(route) <= 10)
.collect(toUnmodifiableList());
}
- 기존 값 변경이 아닌, 복사본을 리턴하도록 수정
이제 코틀린으로 변환
package travelator
import travelator.Collections.sorted
import travelator.Other.SOME_COMPLICATED_RESULT
import java.util.Comparator.comparing
import java.util.stream.Collectors
object Suffering {
@JvmStatic
fun sufferScoreFor(route: List<Journey>): Int {
return sufferScore(
longestJourneysIn(route, 3),
Routes.getDepartsFrom(route)
)
}
@JvmStatic
fun longestJourneysIn(
journeys: List<Journey>,
limit: Int
): List<Journey> {
val actualLimit = Math.min(journeys.size, limit)
return sorted(
journeys,
comparing { obj: Journey -> obj.duration }.reversed()
).subList(0, actualLimit)
}
fun routesToShowFor(itineraryId: String?): List<List<Journey>> {
return bearable(Other.routesFor(itineraryId))
}
private fun bearable(routes: List<List<Journey>>): List<List<Journey>> {
return routes.stream()
.filter { route -> sufferScoreFor(route) <= 10 }
.collect(Collectors.toUnmodifiableList())
}
private fun sufferScore(
longestJourneys: List<Journey>,
start: Location
): Int {
return SOME_COMPLICATED_RESULT()
}
}
@JvmStatic
fun longestJourneysIn(
journeys: List<Journey>,
limit: Int
): List<Journey> {
val actualLimit = Math.min(journeys.size, limit)
return sorted(
journeys,
comparing { obj: Journey -> obj.duration }.reversed()
).subList(0, actualLimit)
}
======================================================
@JvmStatic
fun longestJourneysIn(journeys: List<Journey>, limit: Int): List<Journey> =
journeys.sortedByDescending { it.duration }.take(limit)
======================================================
// 확장함수
@JvmStatic
fun List<Journey>.longestJourneys(limit: Int): List<Journey> =
sortedByDescending { it.duration }.take(limit)
- 확장함수는 클래스에 새로운 함수를 추가하는 것.
@JvmStatic
fun sufferScoreFor(route: List<Journey>): Int {
return sufferScore(
route.longestJourneys(limit = 3), // 확장함수 호출
Routes.getDepartsFrom(route)
)
}
- 확장함수 호출하도록 수정한다.
private fun bearable(routes: List<List<Journey>>): List<List<Journey>> {
return routes.stream()
.filter { route -> sufferScoreFor(route) <= 10 }
.collect(Collectors.toUnmodifiableList())
}
======================================================
private fun bearable(routes: List<List<Journey>>): List<List<Journey>> =
routes.filter { sufferScoreFor(it) <= 10 }
- list 가 filter 를 확장함수로 제공
- List 반환하기에 toUnmodifiableList 필요 없음
반응형
'개발 > java basic' 카테고리의 다른 글
일급 컬렉션이란? (0) | 2024.11.13 |
---|---|
제네릭이란 무엇일까 (0) | 2024.11.12 |
자바에서 코틀린 - 5장 빈에서 값으로 (0) | 2023.04.13 |
자바 기본 ( 18 ) - 예외처리 (0) | 2020.11.03 |
자바 기본 (17) - 추상, 인터페이스 (0) | 2020.10.08 |
Comments