Scanner 클래스 짱짱맨하고 형변환이 필요없는 Scanner 클래스를 쓰고 있는 와중에 PS를 풀다보면 종종 시간초과로 입출력 방식을 달리해야할 필요가 있음을 느끼게 된다.
그때그때마다 찾아서 문제를 풀어보고는 있지만 성능 개선에 대한 관심이 조금은 있는 본인이기에 Java에서의 입력 방식을 정리해보고자 한다.
Java에서 가장 먼저 배우고 많이 쓰는 기본적인 입력 클래스이자 제일 최근에 나온 클래스
Scanner sc = new Scanner(System.in);
int n = sc.nextInt(); // int
long l = sc.nextLong(); // int
String s = sc.next(); // String
String s = sc.nextLine(); // String
일반적인 경우에는 Scanner를 이용하는 것이 매우 편하다. 하지만 문자열의 경우 어떨까?
//한 줄 입력 - 1 2 3 4 5 6 7 8 9 10 11 12
for(int i=0;i<12;i++){
sc.nextInt();
}
기존 Scanner 클래스의 경우, nextInt()를 입력 개수만큼 호출해주면 된다.
하지만 입력이 10000번, 10만번 등등 매우 큰 경우를 생각하면 매번 nextInt()를 호출해줘야 한다는건데 생각만해도 정신이 아찔하다...
이에 문자열 입력에는 이전에 나온 BufferedReader와 StringTokenizer가 사용된다.
//한 줄 입력 - 1 2 3 4 5 6 7 8 9 10 11 12
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
String[] s = br.readLine().split(" ");
// s[0] = "1"; Integer.parseInt(s[0]) => 1
// s[1] = "2";
// s[2] = "3";
// .....
문자열로 받고 split 메소드를 이용해 공백을 분리자로 하여 받는 것이다.
또한 값은 String으로 받았기에 Integer.valuOf()를 이용하여 형변환하여 사용하면 충분
물론 Scanner 클래스의 nextLine() 메소드를 이용하여 동일한 방법으로 코드를 작성할 수 있으나 입력 값의 단위가 크면 클수록 크게 차이가 난다고 하니
다양한 기능 -> Scanner 클래스 / 대량 문자열 -> BufferedReader 클래스로 머리 속에 기억해두면 될 듯하다.
BufferedReader와 같이 문자열을 받는데 다른 점으로 BufferedReader는 잘라서 배열화하여 index를 사용하여 접근하는 것과 달리 StringTokenizer는 공백 뒤의 문자열이 공백을 땡겨 채우며 값이 저장된다. -> split이 필요 없다는 것
BufferedReader br = new BufferedReader(new InputStreamReader(System.in);
StringTokenizer st = new StringTokenizer(br.readLine());
// AB CDD EFFF GH 입력
st.nextToken() // AB
st.nextToken() // CDD
st.nextToken() // EFFF
st.nextToken() // GH
위와 같이 StringTokenizer를 쓸 수 있다.
결국 문자열을 잘라서 쓰는 건 같은데 무슨 차이가 있는가? 하면 기본적으로 split 메소드를 쓰지 않는데서 큰 차이가 발생한다.
split 메소드는 정규식을 기반으로 자르는 로직이기에 내부에서 복잡한 처리가 필요하나 nextToken은 단순히 공백 자리를 채우는 것이기에 속도 차이가 날 수 밖에 없다.
적은 입력 값 및 다양한 활용 필요 -> Scanner()
많은 입력 값 및 인덱스 접근 처리 필요 -> BufferedReader()
많은 입력 값 -> StringTokenizer()
데이터 소스로부터 더이상 읽을 수 있는 데이터가 없음을 의미
입력 값의 수를 명시하지않은 경우 EOF 처리가 필요하다.
다음은 입력 방식에 따른 EOF 처리를 적어보았다.
Scanner sc = new Scanner(System.in);
while(sc.hasNextLine()) { //입력되는 형식에 따라 달리 작성
sc.nextLine();
}
while(sc.hasNextInt()) {
sc.nextInt();
}
BufferedReader br = new BufferedReader(new InputStreamReader(System.in);
String input = "";
while((input = br.readLine()) != null) {
//......
}
StringTokenizer의 경우 문자열을 입력하는 것이 아닌 입력받은 문자열을 가공하는 역할로 EOF를 처리한다고 보기 어렵다.
참고한 사이트
https://mygumi.tistory.com/78
https://mygumi.tistory.com/236