안녕하세요 :)
오늘은 JAVA의 Scanner를 사용하며 부딪힌 문제에 대해 공유해보고자 합니다.
저는 요새 알고리즘에 빠져 있는데요.
어느 날은 너무 머리가 아파서 낮은 레벨의 문제를 풀고 쉬려 했습니다.
저는 아래의 백준 2751 문제... 실버V 문제(비교적 난이도가 낮은 문제)를 빠르게 풀고 쉬려고 했으나...
계속해서 시간 초과라는 결과를 맞이했습니다 :(
... ㅠㅠ
어느 부분에서 시간을 잡아먹었지?
저는 다음과 같은 순서로 최적화를 진행해봤습니다.
- 변수 줄이기
- 정렬 방식 최적화(기존에 사용한 O(n^2)의 시간 복잡도를 가진 방식을 O(nlogn)의 시간 복잡도를 가진 정렬 알고리즘으로 변경)
결과는... 시간 초과
도대체 뭐가 문제지?
생각하다, "설마.. 입출력 방식이 문젠가?"라는 생각이 들었습니다.
알린이(알고리즘 어린이)인 제게는 알고있는 입출력을 기능을 가진 클래스는 Scanner
, System.out.print
뿐이었기에 다른 입력 방식이 있다는 예외는 존재하지 않았습니다.
때문에 저는 다른 입출력 방식을 찾아보았습니다.
백준 입출력 속도 비교
백준 입출력 속도 비교를 확인해보면, 아래와 같이 Scanner
보다 BufferReader
를 사용한 방식이 8배는 빠른 것을 볼 수 있습니다.
이제 본격적으로 향상된 입출력 클래스를 사용해보도록 하겠습니다.
BufferedReader/BufferedWriter 사용해보기
클래스 이름에서 유추할 수 있듯, 다음과 같이 기능이 매칭됩니다!
BufferedReader
-> ScannerBufferedWriter
-> System.out.println();
import 클래스
IntelliJ를 사용하시는 분들은 아마 자동 임포트가 되겠지만, 아닌 분들을 위해 리스트업 해드리겠습니다!
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
BufferdReader/BufferdWriter
BufferedReader 주의사항
- 입력 스트림 종료 처리
BufferedReader.readLine()
을 사용할 때, 입력의 끝을 처리하는 방법을 고려해야 합니다. readLine()
이 null
을 반환하면 입력의 끝을 의미합니다.
- 예외 처리
입력을 읽는 동안 발생할 수 있는 예외IOException
를 적절히 처리해야 합니다.
- 리소스 해제
사용이 끝난 후 BufferedReader
를 반드시 닫아야 합니다. 이를 위해 try-with-resources
구문을 사용하는 것이 좋습니다.
BufferedWriter 주의사항
- 버퍼 플러시
버퍼에 남아 있는 데이터를 모두 출력하기 위해 flush()
메서드를 호출해야 합니다. 그렇지 않으면 버퍼에 남아 있는 데이터가 출력되지 않을 수 있습니다.
- 개행 문자 처리
BufferedWriter.write()
메서드는 개행 문자를 자동으로 추가하지 않습니다. 따라서 줄 바꿈을 원한다면 명시적으로 개행 문자를 추가해야 합니다.
- 리소스 해제
사용이 끝난 후 BufferedWriter
를 반드시 닫아야 합니다. 이를 위해 try-with-resources
구문을 사용하는 것이 좋습니다.
BufferedRead
/BufferedWriter
를 사용하여 2751문제 풀어보기
try-with-resources
구문을 사용하지 않는 경우
이 경우에는 finally
블록에서 close()
를 명시적으로 호출하여 리소스를 해제해줘야 합니다.
이렇게 사용해야 예외가 발생해도 리소스를 제대로 닫을 수 있습니다.
package sort;
import java.io.*;
import java.util.*;
public class 수정렬하기2751 {
public static void main(String[] args) {
BufferedReader bf = null;
BufferedWriter bw = null;
try {
bf = new BufferedReader(new InputStreamReader(System.in));
bw = new BufferedWriter(new OutputStreamWriter(System.out));
int n = Integer.parseInt(bf.readLine());
List<Integer> list = new ArrayList<>(n);
for (int i = 0; i < n; i++) {
list.add(Integer.parseInt(bf.readLine()));
}
Collections.sort(list);
for (int i = 0; i < n; i++) {
bw.write(list.get(i) + "\n"); // System.out.println()과 같은 자동개행 기능이 없기 때문에 개행을 해줌
}
bw.flush();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (bf != null) bf.close();
if (bw != null) bw.close();
} catch (IOException ex) {
ex.printStackTrace();
}
}
}
}
try-with-resources
구문을 사용하는 경우
이 경우에는 try-with-resources
구문 덕분에 BufferedReader
와 BufferedWriter
가 블록을 벗어날 때 자동으로 닫히기 때문에 close()
를 명시적으로 호출할 필요가 없습니다.
package sort;
import java.io.*;
import java.util.*;
public class 수정렬하기2751 {
public static void main(String[] args) {
// try-with-resources를 사용하여 리소스를 자동으로 해제
try (BufferedReader bf = new BufferedReader(new InputStreamReader(System.in));
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out))) {
int n = Integer.parseInt(bf.readLine());
List<Integer> list = new ArrayList<>(n);
for (int i = 0; i < n; i++) {
list.add(Integer.parseInt(bf.readLine()));
}
Collections.sort(list);
for (int i = 0; i < n; i++) {
bw.write(list.get(i) + "\n");
}
// 모든 데이터를 출력한 후 flush를 호출
bw.flush(); // 버퍼에 남아 있는 데이터를 출력(버퍼를 비워야 하기 때문)
} catch (IOException e) {
e.printStackTrace(); // 예외 발생 시 스택 트레이스를 출력
}
}
}
'Back-Language > Java' 카테고리의 다른 글
[Java] 중복을 허용하지 않는 컬렉션 - SET (0) | 2024.07.16 |
---|---|
[Spring] 테스트 케이스 given - when - then 패턴 (0) | 2024.07.09 |
[Java] String, 리터럴 객체의 메모리 할당(2) - 불변의 String...? (0) | 2024.04.21 |
[Java] String, 리터럴 객체의 메모리 할당(1) (0) | 2024.04.21 |