제가 가장 좋아하는 언어인 자바는, 개발하기에는 너무 좋지만.. 알고리즘 풀기에는 여러모로 생각해야 할 게 많은 언어인데요.. 특히 다른 건 모르지만, 알고리즘 문제를 풀 때 입출력에 있어서는 신경 써야 할 게 많은 아이랍니다 🤣🤣🤣
int main() {
string input;
cin >> input; // 공백 전까지만 입력됨
cout << "입력된 문자열: " << input << endl;
return 0;
}
C++은 언어 자체가 어렵긴 하여도 성능 자체가 굉장히 우수하기로 유명하고 입출력에서도 특별한 방법이 아닌 표준적인 방법만으로도 거의 모든 문제를 커버할 수 있습니다.
input_str = input("문자열을 입력하세요: ")
print(f"입력된 문자열: {input_str}")
파이썬도 마찬가지로 성능은 상대적으로 낮을지 몰라도 입출력에 있어서 다른 특별한 방법이 아니고서는 input()
, print()
함수만으로도 거의 모든 문제 커버가 가능합니다!
하지만 우리 자바는 조금 다릅니다. 자바를 배우시면 다들 아시겠지만 자바의 I/O 문법을 처음 배우면 사용하게 되는 Scanner()
메서드와 자주 사용하는 System.out.print()
메서드를 통해서 입출력을 사용하게 됩니다.
public class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
System.out.print("문자열을 입력하세요: ");
String str = scanner.nextLine();
System.out.println("입력된 문자열: " + str);
}
}
자바는 위의 두 언어와는 다르게 시작부터 클래스 안에 main 메서드를 만들어 주고 그 안에 Scanner 객체를 생성할 때 매개변수로 System.in
을 넣어줘야 하고 System.out.print()
메서드로 출력을 한 번 해주고 다시 또 스캐너 객체로 입력을 한 줄 받은 다음에 다시 또 print 메서드를 통해 출력까지 해주어야 합니다.
물론 저와 같이 자바에 익숙하신 분들이라면 아무런 생각도 들지 않으시겠지만 자바가 처음이거나 익숙하지 않으신 분들은 입출력 예제 하나 하는데도 굉장히 번거로워 보이실 거라고 생각이 듭니다. 저도 처음 자바 언어를 시작할 때… 책을 펼쳤을 때 자바의 문법을 보고 다시 책을 덮고 싶다는 생각을 한 적이 있었습니다 😢
심지어 scanner()
와 print()
말고도 다른 입출력 방법들이 있습니다. 심지어 성능이 더 좋은 방법이 있습니다. 그래서 저같이 성능충(?)은 스캐너를 마지막으로 사용한 게 언제인지 기억도 안나고.. 요즘은 print()
메서드도 잘 사용하지 않습니다…🫢
사실 간단한 입출력 정도는 빠르게 해결할 수 있는 scanner()
와 print()
가 제격이지만 그런데도 여유가 있다면 더 좋은, low 한 방법을 사용해 보는 것도 좋습니다.
버퍼(Buffer)는 컴퓨터 프로그래밍에서 매우 중요한 개념 중 하나입니다. 버퍼는 데이터를 임시로 저장하는 메모리 영역을 의미합니다.
- 데이터는 프로그램의 한 부분에서 다른 부분으로 전송될 때 일반적으로 버퍼를 통해 전송됩니다.
- 각기 다른 속도로 동작하는 두 장치나 프로세스 사이에서 데이터를 전송할 때 버퍼를 사용합니다. 이는 빠른 속도로 데이터를 생성하거나 소비하는 프로세스와 느린 속도로 데이터를 생성하거나 소비하는 프로세스 사이의 속도 차이를 극복하는 데 도움이 됩니다.
- 예를 들어, 사용자가 키보드를 통해 입력하는 데이터는 운영체제의 키보드 입력 버퍼에 저장됩니다. 이 버퍼는 사용자가 입력한 순서대로 데이터를 저장하며, 프로그램은 이 버퍼에서 데이터를 가져와 처리합니다.
BufferedReader
는 자바에서 제공하는 버퍼를 이용한 입력 스트림 클래스입니다.BufferedReader
는 내부에 버퍼를 가지고 있어, 문자형 데이터를 버퍼에 임시로 저장한 후 한 번에 읽음으로써 입력 효율을 높입니다.
BufferedReader
는 문자를 내부 버퍼에 저장하고, 버퍼가 비거나 read()
메서드가 호출될 때 버퍼의 내용을 한 번에 읽습니다. 이렇게 하면 각 문자를 개별적으로 읽는 것보다 입출력 효율이 향상됩니다.BufferedReader
는 입력 스트림 클래스로, 주로 파일에서 문자형 데이터를 읽는 데 사용됩니다.
Scanner
도 입력 스트림 클래스로, 주로 키보드 입력이나 파일로부터 데이터를 읽는 데 사용됩니다.
데이터를 읽을 때마다 입출력이 발생하는 Scanner
와 달리, BufferedReader
는 버퍼가 비거나 read()
메서드가 호출될 때 버퍼의 모든 내용을 한 번에 읽습니다. 이렇게 함으로써 각 문자를 개별적으로 읽는 것보다 입출력 횟수를 크게 줄일 수 있습니다.
따라서, BufferedReader
는 버퍼를 이용한 배치 처리 방식 덕분에 Scanner
보다 효율적으로 읽기 성능을 제공하기 때문에 속도가 빨라집니다.
public class BufferedReaderExample {
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
System.out.print("문자열을 입력하세요: ");
String input = br.readLine();
System.out.println("입력된 문자열: " + input);
br.close();
}
}
BufferedWriter
는 자바에서 제공하는 버퍼를 이용한 출력 스트림 클래스입니다. 문자형 데이터를 버퍼에 임시로 저장한 후 한 번에 출력함으로써 출력 효율을 높입니다.
BufferedWriter
도 동일하게 내부에 크기가 정해진 버퍼를 가지고 있습니다. 문자 데이터를 이 버퍼에 저장하고, 버퍼가 가득 차거나 flush()
메서드가 호출될 때 버퍼의 모든 내용을 한 번에 출력합니다.BufferedWriter
는 파일에 문자형 데이터를 쓰는 데 주로 사용됩니다. 내부적으로 버퍼를 사용하여 데이터를 한 번에 모아서 출력합니다.
System.out.print()
는 콘솔에 문자열을 출력하는 데 주로 사용됩니다. 내부적으로 버퍼를 사용하지 않으므로, 호출될 때마다 즉시 출력됩니다. 따라서 System.out.print()
는 BufferedWriter
보다 더 간단한 출력 작업에 적합합니다.
System.out.print()
는 호출될 때마다 입출력이 발생하지만, BufferedWriter
는 버퍼가 가득 찰 때까지 데이터를 모아놓고 한 번에 출력합니다. 이로 인해 입출력 횟수가 줄어들어 속도가 빨라지게 됩니다.
따라서, BufferedWriter
는 버퍼를 이용한 배치 처리 방식 덕분에 System.out.print()
보다 더 적은 입출력 횟수로 데이터를 출력할 수 있어 빠른 성능을 제공합니다.
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(); // BufferedWriter를 닫아서 자원을 해제합니다.
}
}
시너지 효과 !!
BufferedReader
와BufferedWriter
는 모두 내부적으로 버퍼를 사용하기 때문에 데이터를 한 번에 모아서 읽거나 쓸 수 있도록 함으로써 입출력 효율을 크게 향상시킬 수 있습니다. 특히 대량의 데이터를 처리할 때 유용합니다 👍🏻
Scanner
와 System.out.print()
는 각각의 호출마다 입출력을 수행하기 때문에 상대적으로 비효율적인 수행을 합니다.BufferedReader
와 BufferedWriter
는 다른 스트림과 결합하여 사용할 수 있습니다. 이를 통해 다양한 입출력 요구 사항을 유연하게 처리할 수 있지만, 반면에 Scanner
와 System.out.print()
는 이러한 유연성을 제공하지 않습니다.public class BufferedReaderWriterExample {
public static void main(String[] args) throws Exception {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
String line = "";
while ((line = br.readLine()) != null) {
bw.write(line);
bw.newLine();
}
bw.flush();
br.close();
bw.close();
}
}
위의 차이를 보시다시피 내부적으로 버퍼를 사용하여 데이터를 한 번에 모아서 읽는 것은 각 문자나 줄을 개별적으로 읽는 것보다 입출력 횟수를 크게 줄여주었기 때문에BufferedReader
를 사용하면 동일한 양의 데이터를 처리하는 데 필요한 시간이 Scanner
에 비해 상당히 단축됩니다 ⏱️⏱️⏱️
입력 데이터 양에 비례하여 상당히 많은 양의 출력을 수행해야 하거나, 입출력이 성능에 큰 영향을 미치는 상황이라면 BufferedWriter
를 사용하는 것이 바람직합니다만.. 대부분의 알고리즘 문제에서는 결과를 모아서 마지막에 한 번 출력하는 경우가 많기 때문에 System.out.print
과 BufferedWriter
사이에 성능 차이는 크게 중요하지 않을 것이라 생각합니다.
무엇보다 문자열을 복잡하게 다루어야 하는 상황이 발생할 경우에는 StringBuilder를 활용하여 문자열을 모으고 이를 한 번에 출력하면, BufferedWriter
를 사용하는 것과 System.out.print()
을 사용하는 것 사이에 별다른 성능 차이가 없습니다. 그리고 StringBuilder를 사용하면 출력하고자 하는 문자열을 모아서 관리할 수 있어, 코드의 가독성과 간결함도 향상시킬 수 있습니다.
간단한 문제 혹은 출력량이 크게 문제되지 않는 경우엔 System.out.print()
만으로 충분하지만, 출력이 많은 경우 BufferedWriter
를 사용하거나 StringBuilder
를 적절히 사용하는 것을 추천드립니다👍🏻
(물론 저도 Sout 을 종종 사용하지만 그래도 버퍼 형태를 자주 사용하려고 합니다)
[참고]