예제
무작위 정수 하나를 생성하고 싶을 때, 프로그래머가 직접 메소드를 만들었다고 하는 경우가 있을 것이다.
[코드 59-1 흔하지만 문제가 심각한 코드!]
static Random rnd = new Random();
static int random(int n) {
return Math.abs(rnd.nextInt()) % n;
}
괜찮은 듯 보여도 문제를 세 가지나 내포하고 있다.
- n이 그리 크지않은 2의 제곱수라면 얼마 지나지 않아 같은 수열이 반복된다.
- n이 2의 제곱수가 아니라면 몇몇 숫자가 평균적으로 더 자주 반환된다.
public static void main(String[] args) {
int n = 2 * (Integer.MAX_VALUE / 3);
int low = 0;
for(int i = 0; i < 1000000; i++) {
if(random(n) < n / 2) low++;
}
System.out.println(low);
}
결과
- 기대한 값은 약 50만 이지만, 실제로 돌려보면 666,666에 가까운 값을 얻는다. 실제로 돌려보면 666,6666에 가까운 값을 얻는다.
- 즉, 무작위로 생성된 수 중에서 2/3 가량이 중간값보다 낮은 쪽으로 쏠린 것이다.
- 지정한 범위 '바깥'의 수가 종종 튀어 나올 수 있다.
- rnd.nextInt() 가 반환한 값을 Math.abs를 이용해 음수가 아닌 정수로 매핑하기 때문이다.
- nextInt()가 Integer.MIN_VALUE를 반환하면 Math.abs 가 Interger.MIN_VALUE를 반환하고 나머지 연산자가 음수를 반환해버린다.
System.out.println(Math.abs(Integer.MIN_VALUE) % 11);
그럼 어떻게 해야할까?
- 직접 해결하기 위해서는 의사난수 생성기, 정수론, 2의 보수 계산 등에 조예가 깊어야 한다.
- 하지만 라이브러리를 사용하면 이를 몰라도 쉽게 해결할 수 있다.
- 위의 예제에서는 Random.nextInt(int)를 사용하면 된다.
참고) 자바 7부터는 Random보다 성능이 좋은 ThreadLocalRandom을 사용하는 것이 좋다.
라이브러리를 사용하여 얻는 이점
- 표준 라이브러리를 사용하면 그 코드를 작성한 전문가의 지식과 여러분보다 앞서 사용한 다른 프로그래머들의 경험을 활용할 수 있다.
- 핵심적인 일과 크게 관련 없는 문제를 해결하느라 시간을 허비하지 않아도 된다.
- 따로 노력하지 않아도 성능이 지속해서 개선된다.
- 기능이 점점 많아진다. 라이브러리에 부족한 부분이 있다면 개발자 커뮤티니에서 이야기가 나오고 논의된 후 다음 릴리즈에 해당 기능이 추가되곤 한다.
- 우리가 작성한 코드가 많은 사람에게 낯익은 코드가 된다.
그 외
- 자바는 메이저 릴리즈마다 주목할 만한 수많은 기능이 라이브러리에 추가된다. 이를 웹페이지에 공시하는데 한번 쯤 읽어볼 만하다.
- 표준 라이브러리가 매우 방대하기 떄문에 모든 API들을 공부하기 어렵겠지만 적어도 java.lang, java.util, java.io 와 그 하위 패키지들에는 익숙해져야 한다.
- 마지막으로 자바의 컬렉션 프레임워크와 스트림 라이브러리, java.util.concurrent의 동시성 기능도 익혀두면 도움이 될 것이다.