https://codingnotes.tistory.com/216
스트림과 버퍼에 대한 포스팅은 위 링크에서 확인하실 수 있습니다.
Scanner
Java에서 키보드로부터 입력을 받는 방법에는 크게 두가지가 있습니다.
Scanner를 이용한 방법과 BufferedReader를 이용한 방법입니다.
Scanner에는 nextInt(), nextLine(), nextDouble(), nextFloat() 등의 메서드가 있습니다.
보시는 것처럼 입력을 받을 때, 자료형을 확정하여 입력 받을 수 있기 때문에, 형변환 과정을 줄일 수 있습니다.
하지만, Scanner는 위와 같은 문제가 발생할 수 있습니다.
위 코드는 Scanner를 사용해서 정수 하나와 문자열 하나를 입력 받는 코드인데, 11만 입력해도 프로그램이 종료됩니다.
nextInt()에서 정수만을 가져오고 개행은 버퍼에 남겨두었기 때문에 문자열을 입력 받을 때 개행만을 가져온 것입니다.
올바르게 동작하기 위해서 주석된 코드처럼 nextLine()을 이용하여 버퍼에서 개행을 제거한 후에, 문자열을 입력 받아야 합니다.
또한, Scanner는 BufferdReader와 비교했을 때, 입력 속도가 매우 느립니다.
Scanner vs BufferedReader
위 이미지는, 1000만개의 숫자를 입력 받을 때의 수행시간입니다.
Scanner와 BufferedReader의 속도가 크게 차이나는 것을 확인할 수 있습니다.
따라서, 입력이 많은 알고리즘을 풀 때는 시간 초과가 발생할 수 있습니다.
그럼 Scanner가 BufferedReader보다 더 느린 이유가 무엇일까요?
먼저, Scanner의 버퍼 사이즈는 1,024개의 문자를 저장할 수 있는 반면, BufferdReader는 기본적으로 8,192개의 문자를 저장할 수 있습니다. 따라서 버퍼 크기가 더 작은 Scanner는 더 빈번히 버퍼를 비워주어야 하기 때문에 속도가 더 느립니다.
또한 Scanner는 입력 받을 때 정규식을 이용하여 입력 값을 파싱합니다.
예를 들면, 1,234,567처럼 숫자 사이에 콤마가 있어도 정규식을 통해 정수로 파싱해줍니다.
즉, 버퍼 사이즈가 작고, 정규식을 이용하여 입력 값을 파싱하는 과정 때문에 Scanner가 BufferedReader보다 더 느린 것입니다.
BufferedReader
BufferedReader를 사용할 때는 보통 readLine()를 이용해서 라인 단위로 입력 받습니다.
readLine()은 개행을 끝으로 하는 문자열을 읽어와서 개행을 제외한 문자열을 반환합니다.
BufferedReader를 사용할 때는 split 또는 StringTokenizer를 사용하여 입력을 쪼개고, 형변환해서 사용하시면 됩니다.
위 코드는 split을 사용하여 키보드로부터 입력을 받는 코드입니다.
먼저 BufferedReader에 문자 스트림인 InputStreamReader와 바이트 스트림인 System.in을 연결시켜줍니다.
그리고 readLine()으로 라인 하나를 읽어주면 되는데요, readLine()의 내부에서 IOException이 발생할 수 있기 때문에 throw 해주면 됩니다.
다음으로는 읽어온 라인을 공백을 기준으로 split하고, 문자열 배열을 반환 받은 다음, 첨자로 접근해서 원하는 타입으로 형변환 해주시면 됩니다.
다음으로, StringTokenizer를 사용한 방법입니다.
마찬가지로, readLine()으로 한 라인을 입력 받고, 구분자로 공백 문자를 전달해주면 됩니다.
이후에는 StringTokenizer 객체의 nextToken()를 호출하면, 구분자로 잘라진 token이 순서대로 반환되기 때문에,
마찬가지로 뱐환 받은 다음 형변환 해서 사용하시면 됩니다.
BufferedWriter
BufferedWriter를 이용하여 콘솔에 출력할 때는 마찬가지로 문자 스트림인 OutputStreamWriter와 바이트 스트림인 System.out을 연결시켜주고, IOException을 throw 해주면 됩니다.
문자열을 출력 할 때는 write()를 호출해주면 됩니다.
개행이 필요할 때는 write 메서드에 개행을 포함한 문자열을 전달해주어도 되고, newLine()를 호출해도 됩니다.
⚠️ BufferedWriter를 사용할 때 주의할 점으로는 두 가지가 있습니다.
출력 버퍼 스트림은 버퍼가 꽉 찼을 때만 출력 되기 때문에 flush()를 호출해주어야 합니다.
또한, 버퍼를 사용하는 BufferedWriter와 System.out.println()를 함께 사용하면 출력이 꼬일 수 있으므로 함께 사용하는 것은 지양하는 것이 좋습니다.
참조
- 명품 자바 프로그래밍 (Chapter 8. 입출력 스트림과 파일 입출력)
- What is the System setIn() function in Java?
- C/C++ 입출력 속도 비교
- [알고리즘] Scanner와 BufferedReader의 속도 차이
- Why is Scanner so much slower than BufferedReader?
- [JAVA] scanner 버퍼 비우기
'Java' 카테고리의 다른 글
Java의 다형성은 어떻게 동작하는가? (0) | 2022.08.19 |
---|---|
Java 입출력 (스트림, 버퍼) (0) | 2022.07.25 |
Java 8 / 11 / 17의 변화 (1) | 2022.07.04 |
JDBC란? (0) | 2020.07.11 |
JSTL(JSP Standard Tag Library)이란? (0) | 2020.07.09 |