본문 바로가기

Algorithm/Algorithm for Beginner

1. Scanner와 BufferedReader로 입력받기

보통 처음 Java를 배울 때 입력방식은 Scanner를 사용하게 됩니다. Scanner는 사용하기에 아주 편리한 클래스 입니다.

Scanner는 공란과 줄바꿈 모두 입력값의 경계로 인식하기 때문에 데이터를 입력받기에 용이하고, 입력받은 즉시 자료형이 확정되기 때문에 문제를 풀어나가기에도 용이합니다.


그에 반하여 BufferedReader 클래스는 일반적으로 라인단위로 입력을 받게 되며, 라인 바이 라인으로 입력값의 경계를 인식하기 때문에 한줄에 공란을 경계로 여러 값이 입력된 경우라면 파싱이 필수적입니다. 또한 입력받은 값은 모두 String 타입이기 때문에 하나하나 타입변환을 해줘야 한다는 불편함이 존재합니다. 더군다나 BufferedReader는 Scanner처럼 자체적으로 Exception에 대한 처리가 되어 있지 않아서, throws Exception 혹은 try ~ catch 를 이용해서 Exception을 따로 처리해줘야 합니다.


그렇다면 '굳이' BufferedReader를 사용하는 이유는 무엇일까요.  바로 속도 때문입니다. 


알고리즘 문제풀이 사이트 BaekJoon Online Judge의 문제를 하나 가져와봤습니다.

출처: https://www.acmicpc.net/problem/2749


문제제목 밑 제일 왼쪽에 시간 제한이 있고 1초라고 적혀 있습니다. 알고리즘의 목표는 같은 결과값이라도 더 빠르고 효율적인 연산을 통해 결과를 도출하는 것이라고 할 수 있습니다, 그렇기 때문에 시간제한을 둬서 효율적인 연산인지 아닌지 평가하게 되어있는 것입니다. 적은 숫자의 입력값이 주어진 경우라면 Scanner도 크게 상관없겠지만, 입력값이 많을 수록 BufferedReader를 사용하는 것은 필수적입니다.


두 클래스가 성능이 어느 정도 차이가 나는지 알고스팟 사이트에 잘 나와있습니다.

URL: https://algospot.com/forum/read/2496/


그러면 Scanner와 BufferedReader 구현 소스를 비교해보겠습니다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import java.util.Scanner;
 
public class ScannerPractice {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
 
        // Integer Type Input
        int intValue = sc.nextInt();
 
        // Long Type Input
        long longValue = sc.nextLong();
        
        // Double Type Input
        double doubleValue = sc.nextDouble();
 
        // String Type Input
        String strValue = sc.next();
 
        // Boolean Type Input
        boolean boolValue = sc.nextBoolean();
    }
}
cs


보기에도 쓰기 간단하고 입력받는 즉시 바로 type이 결정되는 것을 볼 수 있습니다.


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
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.StringTokenizer;
 
public class BufferedReaderTest {
    // BufferedReader는 Exception이 처리를 따로 해줘야 하기 때문에 throws를 해주거나 
    // try ~ catch로 예외처리를 해줘야합니다.
    public static void main(String[] args) throws Exception {
        // BufferedReader 객체 생성
        // new InputStreamReader 및 System.in
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
 
        // StringTokenizer 객체 선언
        StringTokenizer st = null;
 
        // String Line이므로 Integer.parseInt를 이용하여 형변환해야함
        int n = Integer.parseInt(br.readLine());
 
        // "1 3 5 7" 식으로 공란 포함 String Line일시 StringTokenizer 이용
        int[] arrays = new int[n + 1];
        st = new StringTokenizer(br.readLine());
        for (int i = 1; i <= n; i++) {
            // 배열에다 토큰을 하나씩 불러서 입력해줌
            arrays[i] = Integer.parseInt(st.nextToken());
        }
    }
}
cs


그에 반해서 BufferedReader는 그냥 보기에도 복잡해보입니다. BufferedReader의 선언과 InputStreamReader를 이용한 입력값의 처리, Integer 클래스를 이용한 형변환, 라인단위의 입력시 공란을 기준으로 파싱하기 위한 StringTokenizer를 선언 및 토큰처리 등...하지만 빠른 입력을 위해서는 BufferedReader를 습관화해서 사용하시기 바랍니다. 이렇게 복잡한 소스가 정말 Scanner보다 빨라?' 의문이 드시는 분들은 위의 URL에 접속해서 시간적으로 얼마나 차이가 나는지 확인해보시기 바랍니다.