티스토리 뷰
무작위 정수 하나를 생성하고 싶다고 해봅시다.
값의 범위는 0부터 명시한 수 사이입니다.
아주 흔히 마주치는 문제로, 많은 프로그래머가 다음과 같은 짤막한 메서드를 만들곤 합니다.
static Random rnd = new Random();
static int random(int n) {
return Math.abs(rnd.nextInt()) % n;
}
괜찮은 듯 보여도 문제를 세 가지나 내포하고 있습니다.
위 코드의 문제점
[1]. n이 그리 크지 않은 2의 제곱수라면 얼마 지나지 않아 같은 수열이 반복된다.
[2]. n이 2의 제곱수가 아니라면 몇몇 숫자가 평균적으로 더 자주 반환된다.
n 값이 크면 이 현상은 더 두드러집니다.
다음 코드는 무작위 수를 백만개를 생성한 다음, 그 중 중간 값보다 작은 게 몇 개인지를 출력하는 프로그램입니다.
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);
}
random 메서드가 이상적으로 동작한다면 약 50만 개가 출력돼야 하지만, 실제로 돌려보면 666,666에 가까운 값을 얻습니다.
무작위로 생성된 수 중에서 2/3 가량이 중간값보다 낮은 쪽으로 쏠린 것입니다.
[3]. 지정한 범위 '바깥'의 수가 종종 튀어나올 수 있다.
rnd.nextInt()가 반환한 값을 Math.abs를 이용해 음수가 아닌 정수로 매핑하기 때문입니다.
nextInt()가 Integer.MIN_VALUE를 반환하면 Math.abs도 Integer.MIN_VALUE를 반환하고, 나머지 연산자(%)는 음수를 반환해버립니다.
이렇게 되면 프로그램은 실패할 것이고, 문제를 해결하고 싶어도 현상을 재현하기가 쉽지 않을 것입니다.
이 결함을 해결하려면 의사난수 생성기, 정수론, 2의 보수 계산 등에 조예가 깊어야 합니다.
다행히 여러분이 직접 해결할 필요는 없습니다.
Random.nextInt(int)가 이미 해결해놨습니다.
이 메서드의 자세한 동작 방식은 몰라도 됩니다. ⇒ 궁금하다면 API 문서나 소스코드 참고
이 라이브러리가 릴리스된 후 20여 년 가까이 사용됐지만 버그가 보고된 적이 없습니다.
혹시 버그가 발견되더라도 다음 릴리스에서 수정될 것입니다.
표준 라이브러리를 쓰는 이점
[1]. 그 코드를 작성한 전문가의 지식과 여러분보다 앞서 사용한 다른 프로그래머들의 경험을 활용할 수 있다.
자바 7부터는 Random을 더 이상 사용하지 않는게 좋습니다.
ThreadLocalRandom으로 대체하면 대부분 잘 동작합니다.
Random보다 더 고품질의 무작위 수를 생성할 뿐만 아니라 속도도 더 빠릅니다.
한편 포크-조인 풀이나 병렬 스트림에서는 SplittableRandom을 사용합시다.
[2]. 핵심적인 일과 크게 관련 없는 문제를 해결하느라 시간을 허비하지 않아도 된다.
프로그래머들은 하부 공사를 하기보다는 애플리케이션 기능 개발에 집중하고 싶어 합니다.
[3]. 따로 노력하지 않아도 성능이 지속해서 개선된다는 점이다.
사용자가 많고 업계 표준 벤치마크를 사용해 성능을 확인하기 때문에 표준 라이브러리 제작자들은 더 나은 방법을 꾸준히 모색할 수 밖에 없습니다.
자바 플랫폼 라이브러리의 많은 부분이 수 년에 걸쳐 지속해서 다시 작성되며, 때론 성능이 극적으로 개선되기도 합니다.
[4]. 기능이 점점 많아진다는 것이다.
라이브러리에 부족한 부분이 있다면 개발자 커뮤니티에서 이야기가 나오고 논의된 후 다음 릴리스에 해당 기능이 추가되곤 합니다.
[5]. 여러분이 작성한 코드가 많은 사람에게 낯익은 코드가 된다는 것이다.
자연스럽게 다른 개발자들이 더 읽기 좋고, 유지보수하기 좋고, 재활용하기 쉬운 코드가 됩니다.
이상의 이점들에 비춰볼 때 표준 라이브러리의 기능을 사용하는 것이 좋아 보이지만, 실상은 많은 프로그래머가 직접 구현해 쓰고 있습니다.
아마도 라이브러리에 그런 기능이 있는지 모르기 때문일 것입니다.
메이저 릴리스마다 주목할 만한 수많은 기능이 라이브러리에 추가됩니다.
자바는 메이저 릴리스마다 새로운 기능을 설명하는 웹페이지를 공시하는데, 한 번쯤 읽어볼만 합니다.
⇒ Java8-feat, Java9-feat
지정한 URL의 내용을 가져오는 명령줄 애플리케이션을 작성해봅시다.
자바 9에서 InputStream에 추가된 transferTo 메서드를 사용하면 쉽게 구현할 수 있습니다.
public static void main(String[] args) throws IOException {
try { InputStream in = new URL(args[0]).openStream()) {
in.transferTo(System.out);
}
}
라이브러리가 너무 방대하여 모든 API 문서를 공부하기 벅차겠지만 자바 프로그래머라면 적어도 java.lang, java.util, java.io와 그 하위 패키지들에는 익숙해져야 합니다.
언급해둘 만한 라이브러리는 다음과 같습니다.
컬렉션 프레임워크와 스트림 라이브러리(아이템 45, 아이템 46, 아이템 47, 아이템 48)
java.util.concurrent의 동시성 기능도 알아두면 큰 도움이 됩니다.
이 패키지는 멀티스레드 프로그래밍 작업을 단순화해주는 고수준의 편의 기능은 물론, 능숙한 개발자가 자신만의 고수준 개념을 직접 구현할 수 있도록 도와주는 저수준 요소들을 제공합니다.
java.util.concurrent의 고수준 개념은 아이템 80과 아이템 81에서 설명합니다.
때때로 라이브러리가 여러분에게 필요한 기능을 충분히 제공하지 못할 수 있습니다.
더 전문적인 기능을 요구할수록 이런 일이 더 자주 생길 것입니다.
우선은 라이브러리를 사용하려 시도해봅시다.
어떤 영역의 긴으을 제공하는지 살펴보고, 여러분이 원하는 기능이 아니라 판단되면 대안을 사용합시다.
어떤 라이브러리든 제공하는 기능은 유한하므로 항상 빈 구멍이 있기 마련입니다.
자바 표준 라이브러리에서 원하는 기능을 찾지 못하면, 그 다음 선택지는 고품질의 서드파티 라이브러리가 될 것입니다.
적합한 서드파티 라이브러리도 찾지 못했다면, 다른 선택이 없으니 직접 구현합시다.
정리
- 바퀴를 다시 발명하지 말자.
- 아주 특별한 나만의 기능이 아니라면 누군가 이미 라이브러리 형태로 구현해놓았을 가능성이 크다.
- 그런 라이브러리가 있다면, 쓰면 된다.
- 있는지 잘 모르겠다면 찾아보라.
- 일반적으로 라이브러리의 코드는 여러분이 직접 작성한 것보다 품질이 좋고, 점차 개선될 가능성이 크다.
- 여러분의 실력을 폄하하는 게 절대 아니다.
- 코드 품질에도 규모의 경제가 적용된다.
- 즉, 라이브러리 코드는 개발자 각자가 작성하는 것보다 주목을 훨씬 많이 받으므로 코드 품질도 그만큼 높아진다.
'JAVA > 이펙티브 자바' 카테고리의 다른 글
아이템[61]. 박싱된 기본 타입보다는 기본 타입을 사용하라 (0) | 2022.05.14 |
---|---|
아이템[60]. 정확한 답이 필요하다면 float와 double은 피하라 (2) | 2022.05.08 |
아이템[58]. 전통적인 for 문보다는 for-each 문을 사용하라 (0) | 2022.05.08 |
아이템[57]. 지역변수의 범위를 최소화하라 (0) | 2022.05.08 |
아이템[56]. 공개된 API 요소에는 항상 문서화 주석을 작성하라 (0) | 2022.05.07 |
- Total
- Today
- Yesterday
- 디자인 패턴
- 알고리즘
- Spring Boot
- BAEKJOON
- Olympiad
- 이팩티브 자바
- 백준
- MSA
- C++
- 클린 코드
- Spring
- Effective Java
- Algorithm
- 정규표현식
- 이펙티브 자바
- kkoon9
- 객체지향
- 테라폼
- node.js
- kotest
- programmers
- AWS
- Kotlin
- 디자인패턴
- 프로그래머스
- JPA
- 클린 아키텍처
- BOJ
- Java
- 코테
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |