inflearn logo
강의

講義

知識共有

Kevinの分かりやすいRxJava 1部

関数型インタフェースとラムダの概念を把握

함수형 인터페이스 Comparator에 대해

解決済みの質問

397

easyvvon

投稿した質問数 30

0

안녕하세요,

LegacyInterfaceInterfaceExample.java 예제

@Override
public int compare(Car car1, Car car2) {
return car1.getCarPrice() - car2.getCarPrice();
}

또는 LegacyInterfaceToFunctionalInterfaceExample.java의

(car1, car2) -> car1.getCarPrice() - car2.getCarPrice()

의 내부 동작원리가 어떻게 되는지 궁금합니다.

compare 메소드의 리턴 값이 양수, 0, 음수 중의 정수 값일텐데, car1의 price값이 car2의 price값보다 큰 경우(양수)에는 car1 오브젝트가 car2 오브젝트보다 앞에 둔다..이런식인가요?

그리고, List 객체 cars의 각 원소는 어떻게 선택(?)되어 2개의 파라미터로 각각 들어오게 되는건가요?

RxJava Reactive Programming Reactive-Streams 함수형-프로그래밍

回答 2

1

easyvvon

와.. 저는 간단하게라도 답해주셔도 감사한데
이렇게 자세하게 알려주시니 몸 둘 바를 모르겠습니다;;

덕분에 자바뿐만 아니라 정렬 알고리즘에 대해 더 좀 더 꼼꼼히 생각해보고 다시 공부해봐야겠다는 생각을 해봅니다.

답변 너무 감사합니다!

1

Kevin

안녕하세요? 좋은 질문 감사하게 잘 들었습니다.

먼저 Collections.sort()의 내부 동작 원리를 일일이 다 설명 드리기에는 시간이 너무 모자릅니다. 질문자님께서 이해되게 충분한 설명을 하려면 Collections 내부 코드를 일일이 다 설명을 드려야 되는데, 내부 코드를 제가 100 퍼센트 다 완벽하게 이해하고 있는것도 아니기때문에 제가 설명해서 완벽하게 이해시켜드리는건 힘들것 같아요.

다만 "List 객체 cars의 각 원소는 어떻게 선택(?)되어 2개의 파라미터로 각각 들어오게 되는건가요?" 이 질문에 대해서는 대략적으로는 설명을 드릴게요.

이걸 설명 드리려면 내부 코드를 볼 수 밖에 없는데 아무튼 간단하게 살펴보겠습니다.

먼저 Collections.sort( )를 타고 들어가보면 아래 코드를 만날 수 있는데요.

==== List.java ====

default void sort(Comparator<? super E> c) {
Object[] a = this.toArray();
Arrays.sort(a, (Comparator) c);
ListIterator<E> i = this.listIterator();
for (Object e : a) {
i.next();
i.set((E) e);
}
}

Object[ ] a는 cars 리스트를 배열로 바꾼게 되겠죠? 저희가 다시 살펴 보아야 할 부분은 Arrays.sort(a, (Comparator) c); 인데요. 여기를 다시 들어가보겠습니다.

==== Arrays.java ====

public static <T> void sort(T[] a, Comparator<? super T> c) {
if (c == null) {
sort(a);
} else {
if (LegacyMergeSort.userRequested)
legacyMergeSort(a, c);
else
TimSort.sort(a, 0, a.length, c, null, 0, 0);
}
}

 여기에서 정렬 방식이 두가지로 나뉘는데요. 하나는 legacyMergeSort( )이고, 또 하나는 TimSort.sort( )인데, legacyMergeSort( )는 정렬 알고리즘 중에서 병합 정렬을 의미합니다. 그리고 TimSort.sort( ) 는 Tim Peters 라는 분이 만든 정렬 알고리즘인데 Insertion sort(삽입 정렬)와 Merge sort(병합 정렬)를 결합해서 만든 정렬 알고리즘입니다. System Property를 따로 설정하지 않는 이상 Java 에서는 기본적으로 legacyMergeSort( ) 대신에 TimSort.sort( ) 를 사용하는데요. Tim sort가 Merge sort보다 조금 더 나은 성능을 낸다고 하는군요. TimSort 에 대해서는 설명이 잘 되어 있는 별도 링크를 맨 하단에 링크로 남겨 둘게요.

그러면 TimSort.sort( ) 안을 한번 살펴보겠습니다.

==== TimSort.java ==== 

static <T> void sort(T[] a, int lo, int hi, Comparator<? super T> c,
T[] work, int workBase, int workLen) {
assert c != null && a != null && lo >= 0
&& lo <= hi && hi <= a.length;

int nRemaining = hi - lo;
if (nRemaining < 2)
return; // Arrays of size 0 and 1 are always sorted

// If array is small, do a "mini-TimSort" with no merges
if (nRemaining < MIN_MERGE) {
int initRunLen = countRunAndMakeAscending(a, lo, hi, c);
binarySort(a, lo, hi, lo + initRunLen, c);
return;
}


TimSort<T> ts = new TimSort<>(a, c, work, workBase, workLen);
int minRun = minRunLength(nRemaining);
do {
// Identify next run
int runLen = countRunAndMakeAscending(a, lo, hi, c);

// If run is short, extend to min(minRun, nRemaining)
if (runLen < minRun) {
int force = nRemaining <= minRun ? nRemaining : minRun;
binarySort(a, lo, lo + force, lo + runLen, c);
runLen = force;
}

// Push run onto pending-run stack, and maybe merge
ts.pushRun(lo, runLen);
ts.mergeCollapse()
;

// Advance to find next run
lo += runLen;
nRemaining -= runLen;
} while (nRemaining != 0);

// Merge all remaining runs to complete sort
assert lo == hi;
ts.mergeForceCollapse();
assert ts.stackSize == 1;
}

소스 코드가 꽤 복잡해 보이는데요. 결론만 얘기하자면 Tim sort 방식에서는 정렬할 원소의 개수가 일정 기준보다 작을 경우에는 binarySort( )라는 메서드를 호출해서 먼저 정렬을 하는데요. 위 코드에서 첫번째 빨간색으로 된 binarySort( )의 경우 정렬할 배열의 개수가 기준 이하일 경우, merge sort도 필요 없이 binarySort( ) 만으로 정렬을 끝내버리는걸 볼 수 가 있죠.

저희 예제는 정렬할 car 객체의 개수가 5개 밖에 안되기 때문에 binarySort만 진행하고 끝내겠죠?

그럼 binarySort( ) 의 내부 코드를 잠깐 살펴보겠습니다.

==== Timsort.binarySort( ) ===

private static <T> void binarySort(T[] a, int lo, int hi, int start,
Comparator<? super T> c) {
assert lo <= start && start <= hi;
if (start == lo)
start++;
for ( ; start < hi; start++) {
T pivot = a[start];

int left = lo;
int right = start;
assert left <= right;
/*
* Invariants:
* pivot >= all in [lo, left).
* pivot < all in [right, start).
*/
while (left < right) {
int mid = (left + right) >>> 1;
if (c.compare(pivot, a[mid]) < 0)
right = mid;
else
left = mid + 1;
}
assert left == right;


int n = start - left; // The number of elements to move

switch (n) {
case 2: a[left + 2] = a[left + 1];
case 1: a[left + 1] = a[left];
break;
default: System.arraycopy(a, left, a, left + 1, n);
}
a[left] = pivot;
}
}

binarySort란 정확하게 얘기하자면 Binary Insertion Sort(이진 삽입 정렬)인데요. 삽입 정렬의 성능을 개선한 정렬 알고리즘이라고 보시면 되겠습니다. Binary Insertion Sort에 대해서도 관련 링크를 남겨 두도록 할게요.

위 소스 코드에서 저희가 궁금한것은 Comparator의 compare( ) 메서드에 2개의 파라미터가 어떻게 선택되어 입력되느냐 하는 것인데요.

중간 정도에 보면 빨간색으로 된 if(c.compare(pivot, a[mid]) < 0) 이라는 코드가 보이시죠?

질문자님께서 궁금해하시는 부분이 저 코드에 있습니다. ^^;;

Binary Insertion sort 정렬에서는 삽입(Insertion)되어야 할 지점을 찾기 위해 Binary Search(이진 탐색)를 사용하는데요. Binary Search에서는 탐색을 하기 위해 left, right 그리고 mid(기준값)가 필요하다는건 잘 아실거라고 생각을 합니다. 위 코드에서는 Comparator.compare( )를 사용해서 right 값을 찾고 있는걸 보실 수가 있죠? 즉, while문에서 Binary Search가 끝날때까지 계속 루프를 돌면서 Comparator.compare( )로 right 값을 찾고 있는 것입니다. 

이 말을 다시 해보자면 Comparator.compare( ) 에 입력되는 car 객체들이 직접적으로 정렬이 된다기 보다는 특정 정렬 알고리즘에서 정렬을 정상적으로 수행하기 위한 어떤 기준을 찾아주는 역할을 한다고 생각하는게 이해하기 쉬울것 같아요. 이건 제 개인적인 생각이기때문에 다른분들의 의견은 다를수도 있겠지만요.

질문에 대한 답변이 충분한지 솔직히 자신은 없지만 아무튼 학습하시는데 조금이라도 도움이 되셨으면 좋겠네요.

아래는 Tim Sort와 Binary Insertion Sort의 관련 링크이니 한번 읽어보시는것도 괜찮을듯 싶어요.

그럼 좋은 밤 되시고, 또 뵐게요.

- Tim Sort 관련 링크: https://d2.naver.com/helloworld/0315536

- Binary Insertion Search 관련 링크: https://m.blog.naver.com/PostView.naver?isHttpsRedirect=true&blogId=rhaosoversan&logNo=221377149974

Single과 관련해 여쭤보고 싶은 부분이 있습니다!

0

264

2

cold/hot publisher 예제 코드와 관련해 질문 드립니다.

0

363

1

CompletableObserver 클래스의 람다식 표현관련

0

297

1

1강에 예시로 보여주신 ToDoSample 코드에 관해 질문 드립니다!

0

356

1

데이터 결합 연산자 / merge 관련 질문

0

445

1

DROP 배압 전략에 관한 궁금증

0

300

2

map에서의 TimeUtil.sleep에 관한 궁금점

0

216

1

강의 내용을 정리해서 개인 블로그에 올려도 될까요?

0

562

2

TimeUtil.sleep 관련 질문

0

286

1

배압 전략 중에서 DROP 전략과 관련해서 질문 있습니다.

0

335

1

Error 발생 시에도 계속 처리 방법

0

689

2

선언형 프로그래밍과 명령형 프로그래밍

1

784

2

첫번째 강의 부터 이번강의까지 수강하면서 궁금한점 질문드립니다.

0

325

1

안녕하세요. 질문이 있습니다.

1

354

1

logger 가 없는데 util 폴더도 같이 갖다놔야 하나요?

0

327

1

amb 연산자

0

233

1

질문 드립니다.

0

219

1

concatEager( ) 연산자에 관하여

0

318

1

Reactive Streams의 구성요소들과 RxJava의 구성요소들의 관계?

1

397

2

ObservableSequenceEqualExample.java 예제의 delay( ) 연산자 질문있습니다

0

309

3

defer( ), fromFuture( )도 just( )처럼 여러 인자 값을 받을 수 있는지 궁금합니다.

0

264

3

flatMapSingle() 메소드에 대하여

0

620

6

fromFuture() vs fromCallable() 생성 연산자에 대해

0

573

2

Publisher와 Subscriber 간의 프로세스 흐름에 대한 질문

1

340

3