코딩 테스트에서 출력 속도는 생각보다 중요한 요소입니다.
특히 반복문을 통한 대량의 데이터를 출력해야 할 때, 출력 방식에 따라 프로그램의 성능 차이가 발생할 수 있습니다.
이 글에서는 자바에서 자주 사용하는 두 가지 출력 방법인 BufferedWriter와 System.out.print, 그리고 StringBuilder의 차이를 설명하고, 상황에 따라 어떤 방식이 더 적합한지 알려드리려고 합니다!
⭐ BufferedWriter
BufferedWriter는 데이터를 버퍼에 담아 한 번에 출력하는 방식으로, 출력할 데이터가 많을 경우 특히 유용합니다.
한 번에 많은 데이터를 처리하여 I/O 작업의 빈도를 줄이므로 성능이 더 뛰어납니다.
import java.io.*;
public class BufferedWriterExample {
public static void main(String[] args) throws IOException {
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
bw.write("BufferedWriter를 사용한 출력 예시");
bw.newLine(); // 줄 바꿈
bw.write("빠른 출력이 필요할 때 사용합니다.");
bw.flush(); // 남은 데이터를 모두 출력
bw.close(); // 스트림 종료
}
}
장점
- 빠른 출력: 많은 데이터를 처리할 때는 출력 속도가 System.out.print보다 훨씬 빠릅니다.
- 버퍼 사용: 작은 출력 작업이 여러 번 반복되는 상황에서 성능 저하를 방지할 수 있습니다.
단점
- 복잡성: BufferedWriter는 사용 시 flush()나 close()를 명시적으로 호출해야 하므로, 코딩이 약간 더 복잡해질 수 있습니다.
⭐ System.out.print
System.out.print는 자바에서 가장 기본적인 출력 방식입니다. 하지만 출력할 때마다 즉시 콘솔에 데이터를 보내기 때문에, 작은 데이터를 자주 출력할 경우 성능이 떨어질 수 있습니다.
public class SystemOutPrintExample {
public static void main(String[] args) {
System.out.print("System.out.print를 사용한 출력 예시");
System.out.print("\n"); // 줄 바꿈
System.out.print("간단한 출력이 필요할 때 사용합니다.");
}
}
장점
- 간단함: 별도의 추가 작업 없이 바로 사용할 수 있어 간편합니다.
- 즉시 출력: 바로바로 콘솔에 출력되므로, 디버깅할 때 실시간 결과를 확인하기 좋습니다.
단점
- 느린 성능: 많은 데이터를 출력할 때는 BufferedWriter에 비해 성능이 떨어집니다. 특히 반복문에서 자주 호출하면 병목 현상이 발생할 수 있습니다.
⭐ StringBuilder
StringBuilder sb = new StringBuilder();
sb.append("Hello");
sb.append(" World");
System.out.println(sb); // 출력: Hello World
- 장점:
- 문자열을 자주 수정하거나 연결할 때 메모리 효율적.
- 성능이 좋음(String 대신 사용하면 성능 개선).
- 단점:
- 동기화가 없으므로 멀티스레드 환경에서는 안전하지 않음. (이럴 땐 StringBuffer 사용)
✅ StringBuilder vs BufferedWriter vs System.out.print 차이점
- StringBuilder는 메모리 상에서 문자열을 수정하거나 결합할 때 사용하며, 파일이나 콘솔에 직접 출력하지 않습니다.
- BufferedWriter는 파일 출력에 최적화된 방법으로, 많은 양의 데이터를 효율적으로 출력할 때 사용합니다.
- System.out.print는 즉시 콘솔에 출력하는 가장 단순한 방법으로, 소량의 데이터를 출력할 때 사용합니다.
🤔 언제 무엇을 사용해야 할까?
BufferedWriter 를 사용해야 하는 경우
대량의 데이터를 출력해야 하는 경우
예를 들어 코딩 테스트에서 여러 줄을 출력하거나, 반복문을 통해 수백만 개 이상의 데이터를 출력할 때 적합합니다. 버퍼링을 통해 효율적으로 데이터를 처리할 수 있기 때문입니다.
System.out.print 를 사용해야 하는 경우
단일 출력이거나, 출력할 데이터의 양이 적고, 코드의 간결함을 중시할 때.
또한 디버깅할 때 실시간으로 값을 확인하고 싶을 때도 유용합니다.
StringBuilder를 사용해야 하는 경우
문자열을 자주 수정하거나 결합해야 하는 경우
반복문을 통해 여러 문자열을 추가하고, 최종적으로 하나의 문자열로 결합해야 할 때 적합합니다. String은 불변이기 때문에 새로운 문자열이 생성될 때마다 메모리를 할당하지만, StringBuilder는 메모리를 효율적으로 관리하며 문자열을 빠르게 수정할 수 있습니다.
사용 시기 요약
- StringBuilder: 문자열을 자주 수정하거나 조작할 때.
- BufferedWriter: 많은 양의 데이터를 파일에 출력할 때.
- System.out.print: 콘솔에 간단히 출력할 때.
⭐ 백준에서 실제 시간 차이
이 문제는 https://www.acmicpc.net/problem/3460 로 입력값에 따라 출력을 많이 할수도 있는 문제였는데요
역시 출력을 많이 할 수 있어서 그런지, System.out.println보다 StringBuilder와 BufferedReader가 시간복잡도가 더 낮은 것을 확인할 수 있습니다.
하지만 BufferedReader가 외부 데이터를 버퍼링하며 읽어오기 때문에 버퍼 메모리 공간이 추가로 필요합니다. 이로 인해 StringBuilder보다 공간 복잡도가 더 높으나 걸 확인할 수 있습니다.
아래는 코드입니다.
1. StringBuilder를 사용한 코드
import java.io.*;
import java.util.*;
public class Main{
public static void main(String[] args) throws IOException{
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
// BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
StringBuilder sb = new StringBuilder();
int n = Integer.parseInt(br.readLine());
int[] a = new int[n];
for (int i = 0; i<n; i++){
ArrayList<Integer> aa = new ArrayList<>();
a[i]=Integer.parseInt(br.readLine());
while(a[i]>=2){
aa.add(a[i]%2);
a[i]/=2;
}
aa.add(a[i]);
for (int j = 0; j<aa.size(); j++){
if(aa.get(j)==1) sb.append(j+" ");
}
sb.append("\n");
}
System.out.println(sb);
}
}
2. BufferedReader를 사용한 코드
import java.io.*;
import java.util.*;
public class Main{
public static void main(String[] args) throws IOException{
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
int n = Integer.parseInt(br.readLine());
int[] a = new int[n];
for (int i = 0; i<n; i++){
ArrayList<Integer> aa = new ArrayList<>();
a[i]=Integer.parseInt(br.readLine());
while(a[i]>=2){
aa.add(a[i]%2);
a[i]/=2;
}
aa.add(a[i]);
for (int j = 0; j<aa.size(); j++){
if(aa.get(j)==1) bw.write(j+" ");
}
bw.write("\n");
}
bw.flush();
br.close();
}
}
3. System.out.print를 사용한 코드
import java.io.*;
import java.util.*;
public class Main{
public static void main(String[] args) throws IOException{
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
int n = Integer.parseInt(br.readLine());
int[] a = new int[n];
for (int i = 0; i<n; i++){
ArrayList<Integer> aa = new ArrayList<>();
a[i]=Integer.parseInt(br.readLine());
while(a[i]>=2){
aa.add(a[i]%2);
a[i]/=2;
}
aa.add(a[i]);
for (int j = 0; j<aa.size(); j++){
if(aa.get(j)==1) System.out.print(j+" ");
}
System.out.println();
}
}
}
이처럼 코테 문제에서 출력 방식을 어떻게 선택하느냐에 따라 시간 복잡도와 공간 복잡도를 줄일 수 있으니 고민해보고 가장 적합한 방식을 선택하는 것이 중요합니다!
앞으로도 화이팅~!
'코딩 테스트 일지 📒' 카테고리의 다른 글
[프로그래머스] 2021 KAKAO BLIND RECRUITMENT 신규 아이디 추천 | Level.1 | JAVA 💡정규표현식 (0) | 2024.09.27 |
---|---|
[백준] 3460 이진수 | 구현, 수학 | 브론즈 Ⅲ | JAVA 💡25%에 틀렸습니다. 완벽 해결법 + 1등하는 법 (0) | 2024.09.24 |
[백준] 2576 홀수 | 구현, 수학 | 브론즈 Ⅲ | JAVA 💡내가 백준 자바 부문 1등?!?! (2) | 2024.09.23 |
[코테/JAVA] 문자, 문자열 다루기 완벽 가이드: 이 글 하나로 한번에 끝내자! 🔥 (9) | 2024.09.22 |
[코테/JAVA] 배열 & 리스트 완벽 가이드: 이 글 하나로 한번에 끝내자 🔥 (1) | 2024.09.21 |