라이브러리를 익히고 사용하라
우리는 프로그램을 개발할 때 대부분 라이브러리를 이용해 코드를 작성한다.
그 편이 시간적으로나, 효율적으로나 이점이 크기 때문이다. 굳이 이미 있는 기능의 코드를 내가 새로 작성할 필요는 없다.
그러나 편리하고 양질의 코드를 사용할 수 있는 라이브러리는 충분히 익히고 사용해야 한다.
1
2
3
4
5
6
7
8
9
10
11
12
13
List<String> names = new ArrayList<>();
names.add("hong");
names.add("lee");
names.add("kim");
names.add("choi");
names.stream().forEach(name -> {
name = name.toUpperCase();
});
System.out.println(names);
// [hong, lee, kim, choi]
- 위의 예시는 이름 리스트의 이름들을 대문자로 변경하는 로직이다.
- 하지만 스트림 라이브러리를 제대로 익히지 않고 사용한 탓에 원하는 동작이 이루어지지 않는다.
1
2
3
List<String> upperCaseNames = names.stream()
.map(String::toUppercase)
.toList();
- 실제로 원하는 동작을 위해서는 위와 같이 코드를 작성해야 한다.
- 라이브러리를 제대로 익히지 않으면 실수하기 쉽고 오류를 찾기도 힘들다.
라이브러리를 잘못 쓰는 경우
무작위 정수 하나를 생성하고자 할 때 Random 라이브러리를 사용하게 된다.
1
2
3
4
5
static Random rnd = new Random();
static int random(int n) {
return Math.abs(rnd.nextInt()) % n;
}
- 위 코드도 라이브러리를 잘 모른다면 문제가 없어 보인다.
- 하지만 3가지 문제를 가지고 있다.
- n이 그리 크지 않은 2의 제곱수라면, 얼마 지나지 않아 같은 수열이 반복된다.
- 만약 n이 16이라면, nextInt()가 반환하는 정수의 하위 4비트만 사용되어 무작위성이 감소될 수 있다.
- n이 2의 제곱수가 아니라면 몇몇 숫자가 평균적으로 더 반환된다.
- n으로 나누어 떨어지지 않는 경우 일부 값이 다른 값보다 더 반환될 수 있다.
- 지정한 범위를 벗어난 수를 종종 반환한다.
- nextInt()가 Integer.MIN_VALUE를 반환하고 abs()에서도 같은 값을 반환하게 된다.
- 그렇게 되면 나머지 연산에서 음수를 반환한다.
- n이 그리 크지 않은 2의 제곱수라면, 얼마 지나지 않아 같은 수열이 반복된다.
해당 라이브러리를 제대로 파악하지 못했을 때 쉽게 알기 어려운 내용들이다.
1
2
3
4
5
6
7
8
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에 가까운 값을 얻게 된다.
- 나는 667,112가 나왔다.
- 이 결과의 의미는 무작위로 생성된 수 중 2/3 가량이 중간값보다 낮은 쪽으로 쏠렸다는 뜻이다. 즉, 무작위성이 떨어졌다.
라이브러리를 잘 파악했을 경우
오랫동안 사용되어온 라이브러리는 알고리즘에 능통한 개발자들이 설계와 구현, 검증에 시간을 들여 개발한 것이다.
위의 문제는 Random.nextInt(int)로 해결할 수 있다.
1
2
3
static int random(int n) {
return rnd.nextInt(n);
}
- 0에서 n사이의 정수를 반환한다.
- 내부적으로 필요한 범위 내에서 균등한 분포를 보장하고, 음수나 범위를 벗어나는 값을 반환하지 않는다.
- Java 7 이후로는 Random을 사용하지 말고 ThreadLocalRandom으로 대체하면 대부분 잘 동작한다고 한다.
- 속도도 더 빠르다.
- 포크-조인 풀이나 병렬 스트림에서는 SplittableRandom을 사용하면 된다.
표준 라이브러리를 잘 이해하고 사용하면 그 코드를 작성한 전문가의 지식과 우리보다 앞서 사용한 다른 프로그래머들의 경험을 활용할 수 있다.
표준 라이브러리를 사용하는 이점
- 핵심적인 일과 크게 관련 없는 문제를 해결하려 시간을 허비하지 않아도 된다. (생산성 향상)
- 따로 노력하지 않아도 성능이 지속해서 개선된다.
- 기능 또한 점점 많아진다.
- 다른 개발자가 보았을 때 이해도 쉽고 유지보수 또한 쉬워진다.
사실 당연하다. 그리고 실제로 라이브러리를 많이 사용하고 있다고 생각할 수 있다.
하지만 실상은 많은 프로그래머가 필요한 기능을 직접 구현해서 쓰는 경우가 많다고 한다.
라이브러리를 사용하는게 쉽고 좋은걸 아는데도, 그 기능을 가진 라이브러리의 존재를 모르기 때문일 것이다.
따라서 메이저 릴리스마다 주목할만한 수많은 기능이 라이브러리에 추가되는데 이런 부분을 읽어 숙지해두는 것도 좋을 것이다.
1
2
3
4
5
public static void main(String[] args) throws IOException {
try (InputStream in = new URL(args[0]).openStream()) {
in.transferTo(System.out);
}
}
- 지정한 URL의 내용을 가져오는 명령줄 애플리케이션이다.
- 이전에는 작성하기 까다로운 기능이었다고 한다. Java 9부터 InputStream에 추가된 transferTo 메서드를 사용해 쉽게 구현할 수 있다.
위와 같이 흔히 쓰는 라이브러리가 아니거나, 새로운 기능을 개발할 때 필요한 기능이 있는 라이브러리가 존재하는지도 모를 수 있다.
라이브러리는 너무 방대하기 때문에 모든 API 문서를 학습할 수는 없겠지만 책에서는 java.lang, java.util, java.io와 그 하위 패키지들은 숙지하길 권장하고 있다.
다른 라이브러리는 필요 시 익히면 된다.
정리
아주 특별한 기능이 아니라면, 이미 라이브러리로 필요한 기능이 개발되어 있을 것이다.
따라서 그런 라이브러리의 존재를 파악하고, 존재한다면 학습해 사용하면 된다.
라이브러리를 사용한 코드는 코드 품질이 좋고, 생산성 및 성능 향상을 가져다 줄 수 있다.
만약 원하는 표준 라이브러리를 찾지 못했다면, 고품질의 서드파티 라이브러리도 존재하니 최대한 찾아보자. 라이브러리의 활용은 그럴만한 가치가 충분하다.