1. 무슨 문제가 있었는지
기능 개발 이후 테스트 하는 중에 NumberForamtException 이 발생했다. Long.parseLong("주문번호") 메서드를 사용하고 있었고 주문번호는 일반적으로 Long 타입이지만 수동으로 생성한 주문의 경우 랜덤 문자열을 사용한다. 수동으로 생성한 주문 번호를 고려하지 못한 코드였고 이러한 경우 당연히 NumberFormatException 이 발생할 수 있다고 생각했다.
하지만 예외가 발생하는 상황이 일관되지 않다는 것이다. 아래의 코드는 예외가 발생한 코드이고 리스트에 담긴 오더 객체를 오더번호로 정렬하는 로직을 수행하는 도중에 예외가 발생했다.
List<Order> orderList = Lists.of(order1, order2, order3);
orderList.sort(Comparator.comparingLong(Order::getOrderNoToLong)); // 예외가 발생하는 코드
2. 문제의 원인은?
그렇다면 어떤 경우에 NumberForamtException 이 발생하고 어떤 경우에 발생하지 않는 것일까? 그 해답은 sort() 메서드에 있었고 결과적으로 리스트 사이즈의 영향을 받고 있었다. ArrayList의 sort() 메서드는 내부적으로 Arrays의 sort() 메서드를 호출하고 있다. Arrays의 sort() 메서드는 내부적으로 TimSort의 sort() 메서드를 호출한다
ArrayList.sort() → Arrays.sort() → TimSort.sort()
다음은 TimSort 의 sort() 메서드 구현 부분이다. 변수 nRemaining 는 정렬해야 하는 리스트의 사이즈이다. 즉 리스트의 사이즈가 0 또는 1이라면 아무런 정렬을 수행하지 않고 return 하고 있다.
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;
}
// ...
}
그렇다면 리스트의 크기가 2 이상이라면 어떻게 될까? 이러한 경우 countRunAndMakeAscending 메서드가 호출되는데 인자로 comparator(오더 번호로 정렬하기)가 전달되고 있다. 구현 부분을 살펴보면 리스트의 원소를 가지고 우리가 전달한 Comparator 메서드를 호출하고 있다. 우리가 전달한 Comparator는 Long.parseLong() 메서드이고 리스트의 원소에는 문자열이 담긴 경우가 있기 때문에 NumberFormatException 이 발생하는 것이였다.
private static <T> int countRunAndMakeAscending(T[] a, int lo, int hi,
Comparator<? super T> c) {
assert lo < hi;
int runHi = lo + 1;
if (runHi == hi)
return 1;
// Find end of run, and reverse range if descending
if (c.compare(a[runHi++], a[lo]) < 0) { // Descending
while (runHi < hi && c.compare(a[runHi], a[runHi - 1]) < 0)
runHi++;
reverseRange(a, lo, runHi);
} else { // Ascending
while (runHi < hi && c.compare(a[runHi], a[runHi - 1]) >= 0)
runHi++;
}
return runHi - lo;
}
3. 해결 방법
-
4. 배운점
-
5. 출처
-