Knowledge/Java

더 빠르게 입력받고 출력해보자 BufferedReader/BufferedWriter

TakeKnowledge 2019. 9. 4. 22:11
반응형

알고리즘 문제를 풀 때 가장 좋은 입출력 방법은? (C, C++, Java ) ( https://takeknowledge.tistory.com/43 )에도 포스팅했지만 java에서 가장 빠르게 입벽받고 출력할 수 있는 방법은 BufferedReader 와 BufferedWriter를 사용하는거다.

 

입력 속도 비교 ( https://www.acmicpc.net/blog/view/56 ) 에 따르면

첫째 줄에 정수의 개수 N  (= 10,000,000)

둘째 줄부터 N개의 줄에 한 개의 자연수(10,000 이하)가 적힌 파일을 입력받는데 

java에서 무언가를 입력받을 때 주로 쓰는 Scanner는 4.8448초가 걸렸지만

BufferedReader, Integer.parseInt 를 활용했을 때는 0.6585초가 걸렸을 정도로 차이가 크다.

 

또한 출력 속도 비교 ( https://www.acmicpc.net/blog/view/57 ) 에 따르면

총 N개의 줄에 1부터 10,000,000까지의 자연수를 한 줄에 하나씩 출력하는데

java에서 무언가를 출력할 때 System.out.println(i); 은 무려 30.013초가 걸렸지만

BufferedWriter, bf.write(i + "\n"); 를 사용했을 때는 0.9581초가 걸렸을 정도로 이 역시 차이가 크다.

 

결론적으로,  JAVA로 수행 시간이 중요한 알고리즘 문제를 풀때는

Scanner와 System.out.println을 쓰는 것 보다는 BufferedReader/BufferedWriter를 활용하는 게 훨씬 좋다는 얘기

 

사용법도 뭐 Scanner와 System.out.println을 쓸때보다는 번거롭지만 그렇게 까지 복잡하진 않다.

정수 하나를 입력받고 출력하는 예제를 예로 들어보자면

 

1
2
3
4
5
6
7
8
9
10
11
12
13
import java.util.Scanner;
 
public class inputOutput {
    
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        int i = scanner.nextInt();
        
        System.out.println(i);
    }
 
}
 

 

이처럼 구현할 수 있지만 이를 BufferedReader/BufferedWriter를 활용하는 방식으로 바꿔보자면

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
// BufferedReader 와 BufferedWriter를 사용하기 위해 필요한 것들 
// 모두 java.io 패키지에 있는 걸 알 수 있다.
// 따라서 애초에 java.io.* 를 import하고 시작하길 권장한다. 
 
public class inputOutput {
    
    public static void main(String[] args) throws IOException {
                                         // bufferedReader로 문자열을 입력 받으면 try~catch로 예외 처리를 해줘야 한다.
                                         // 그러나 입력받을 때마다 매번 예외처리하는 건 귀찮은 일..
                                         // 사용하는 메소드에 IOException을 throws해 예외처리를 위임하길 권장한다 
        
        BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in));
        
        String s = bufferedReader.readLine();
        // readLine 메소드는 String 값만 받을 수 있다.
        int i = Integer.parseInt(s);
        // 따라서 int 형으로 활용하고 싶다면 형변환이 필요하다
        bufferedReader.close();
        // reader를 사용후엔 스트림을 닫아주자!
        
        
        BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(System.out));
        bufferedWriter.write(i+"\n");
        // bufferedWriter 는 System.out.println 처럼 자동 개행 처리가 되지 않기 때문에 개행 처리를 따로 해줘야한다.
        // bufferedWriter.newLine(); 을 활용하면 개행처리가 가능하나 이건 "\n"에 비해 속도가 느리다고 하니
        // "\n" 을 활용해 개행처리를 하는 걸 권장한다. 또한 여기엔 하나의 장점이 더 있는데
        // bufferedWriter.write(i); 처럼 writer에 int를 넣으면 아스키 코드에 따른 char형 값이 출력된다.
        // 그러나 i와 개행 처리 문자열 "\n" 을 더하면 String 으로 자동 형변환 되기 때문에 
        // 저장되어 있는 int 값 그대로 출력이 가능하다. 여러모로 이 편이 좋다는 얘기 
        
        bufferedWriter.flush();
        // 다 사용했으면 남아있는 데이터를 모두 출력시키고
        bufferedWriter.close();
        // 스트림을 닫아주면 된다.
        
    }
 
}

 

위와 같다. 확실히 코드가 길지만 주석으로 주저리 달아놓은 포인트들을 정리하자면 아래와 같다.

 

BufferedReader 와 BufferedWriter를 사용하려면

 

1.  import java.io.*; 선언

 

2. throws IOException 으로 사용하는 메소드에  예외처리를 위임해라 ( try ~ catch로 해도 되지만.. )

 

3. readLine() 은 String만 리턴한다. 다른 자료형으로 활용하려면 형변환해라.

 

4. reader도 writer도 사용후엔 스트림을 닫아주자.

   writer는 닫기전에 flush를 활용해 남아있는 데이터를 모두 출력시켜야한다.

 

5. 개행처리는 +"\n"을 활용하는 걸 추천한다. newLine() 을 활용할 수도 있지만 개행문자와 비교하면 속도가 느리고

   다른 자료형을 출력할 땐 String으로 형변환도 해주니

   int를 출력했는데 아스키 코드 숫자에 해당하는 문자가 출력되는 일을 막아줄 수도 있다.

반응형