Home Item59 (라이브러리를 익히고 사용하라)
Post
Cancel

Item59 (라이브러리를 익히고 사용하라)

라이브러리를 익히고 사용하라

우리는 프로그램을 개발할 때 대부분 라이브러리를 이용해 코드를 작성한다.

그 편이 시간적으로나, 효율적으로나 이점이 크기 때문이다. 굳이 이미 있는 기능의 코드를 내가 새로 작성할 필요는 없다.

그러나 편리하고 양질의 코드를 사용할 수 있는 라이브러리는 충분히 익히고 사용해야 한다.

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()에서도 같은 값을 반환하게 된다.
      • 그렇게 되면 나머지 연산에서 음수를 반환한다.

해당 라이브러리를 제대로 파악하지 못했을 때 쉽게 알기 어려운 내용들이다.

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와 그 하위 패키지들은 숙지하길 권장하고 있다.

다른 라이브러리는 필요 시 익히면 된다.

정리

아주 특별한 기능이 아니라면, 이미 라이브러리로 필요한 기능이 개발되어 있을 것이다.

따라서 그런 라이브러리의 존재를 파악하고, 존재한다면 학습해 사용하면 된다.

라이브러리를 사용한 코드는 코드 품질이 좋고, 생산성 및 성능 향상을 가져다 줄 수 있다.

만약 원하는 표준 라이브러리를 찾지 못했다면, 고품질의 서드파티 라이브러리도 존재하니 최대한 찾아보자. 라이브러리의 활용은 그럴만한 가치가 충분하다.

This post is licensed under CC BY 4.0 by the author.

Item58 (전통적인 for문보다는 for-each문을 사용하라)

Item60 (정확한 답이 필요하다면 float와 double는 피하라)