신나는 함수 실행 문제를 풀던 중에 코드를 깔끔하게 하려다 메모리는 두 배, 시간은 세 배 걸리는 일이 있어서 원인을 찾아보았다.
두 코드의 다른 점은 한 줄이었다. 밑의 두 코드 출력결과는 동일하다.
sb.append("w(").append(a).append(", ").append(b).append(", ").append(c).append(") = ").append(funcW(a,b,c)).append("\n");
sb.append(String.format("w(%d, %d, %d) = %d\n",a,b,c,funcW(a,b,c)));
그래서 도대체 String.format은 어떤 과정을 거치는지 확인해봤다.
String.format()의 동작 과정
1. String.java
1) 새 Formatter 객체를 생성한다. Formatter()의 format함수를 불러온 뒤, 그 결과를 String으로 변환한다(toString).
public static String format(String format, Object... args) {
return new Formatter().format(format, args).toString();
}
2. Formatter.java
1) String에서 부른 format함수는 다시 Locale도 파라미터로 받는 오버로딩 format함수를 부른다. Locale은 특정 지역(지리적, 정치적, 문화적)을 나타내는 객체다. 프린트할때 지역적 특성을 고려하기 때문에 이런 과정을 거친다.
public Formatter format(String format, Object ... args) {
return format(l, format, args);
}
2) 입력받은 문자열을 하나하나 쪼개서 탐색한다. 구분자가 나오면 fs.print을 호출하는데, 그 메서드는 다시 변환을 위해 여러 개의 메서드를 거치다가 마지막 과정으로 StringBuilder에 append한다.
public Formatter format(Locale l, String format, Object ... args) {
ensureOpen();
// index of last argument referenced
int last = -1;
// last ordinary index
int lasto = -1;
FormatString[] fsa = parse(format);
for (int i = 0; i < fsa.length; i++) {
FormatString fs = fsa[i];
int index = fs.index();
try {
switch (index) {
case -2: // fixed string, "%n", or "%%"
fs.print(null, l);
break;
case -1: // relative index
if (last < 0 || (args != null && last > args.length - 1))
throw new MissingFormatArgumentException(fs.toString());
fs.print((args == null ? null : args[last]), l);
break;
case 0: // ordinary index
lasto++;
last = lasto;
if (args != null && lasto > args.length - 1)
throw new MissingFormatArgumentException(fs.toString());
fs.print((args == null ? null : args[lasto]), l);
break;
default: // explicit index
last = index - 1;
if (args != null && last > args.length - 1)
throw new MissingFormatArgumentException(fs.toString());
fs.print((args == null ? null : args[last]), l);
break;
}
} catch (IOException x) {
lastException = x;
}
}
return this;
}
StringBuilder.java
append 과정은 지난 글에서 같이 다뤘기 때문에 이번에는 작성하지 않겠다.
결론
하나하나 메서드의 동작 과정을 살펴보니 당연히 내가 직접 StringBuilder의 append를 나열해 포맷을 만드는 것보다 String.format이 오래걸리는 게 당연했다. 특히 format()에서 한 글자 한 글자 탐색하는 것에서 성능이 엄청나게 떨어질 수 밖에 없다.
'기록 > JAVA' 카테고리의 다른 글
[JAVA] 예외처리의 비용 (0) | 2022.04.30 |
---|---|
[JAVA] library, framework, pattern, architecture 차이 (0) | 2022.04.19 |
[JAVA] 전위연산, 후위연산 메모리 및 속도 차이 (0) | 2022.03.14 |
[JAVA] StringBuilder의 append(), String의 + 연산자 속도차이 (0) | 2022.03.11 |
[JAVA] StringBuilder의 insert, append (0) | 2022.03.10 |
댓글