Java로 코딩테스트를 보거나 입력을 사용해야 할 때 Scanner 클래스를 사용하면 편리하지만 속도가 느리다는 단점이 있습니다.
그렇기 때문에 속도가 빠른 BufferReader 클래스를 사용을 하면 시간복잡도 효율성에서 유리합니다.
Scanner란 ❓
import java.util.Scanner;
Scanner sc = new Scanner(System.in);
Scanner란 사용자로부터 입력을 받을 수 있도록 도와주는 클래스입니다.
Scanner는 다음과 같은 특징을 가집니다.
- 기본적인 데이터 타입을 모두 받을 수 있습니다.
- 토큰을 기준으로 데이터를 입력받습니다. (공백(띄어쓰기) 및 개행(줄 바꿈)을 기준으로 읽는다.' ', '\t', '\r', '\n' 등)
- 데이터를 입력받을 경우 즉시 사용자에게 전송되며 입력받을 때마다 전송되어야 하기에 많은 시간이 소요됩니다.
- 다양한 기능을 지원하기 때문에 무겁다.
BufferedReader란 ❓
버퍼(Buffer)란 데이터를 전송하기 전에 일시적으로 데이터를 보관하는 메모리의 영역입니다.
Scanner 클래스는 키보드에 입력하는 순간 그 즉시 바로 모니터에 출력됩니다.
Buffer를 사용하게되면 일정 조건을 만족할 때까지 Buffer에 일시적으로 데이터를 보관했다가 조건(엔터)을 만족하면 Buffer를 한꺼번에 전송합니다. 따라서 전송 시간이 줄어 성능이 향상되게 되는 것입니다.
단순 입력의 양이 많아질수록 빛을 발하는 입력 클래스는 BufferedReader입니다.
BufferedReader 사용 방법
먼저 BufferedReader 클래스를 사용하기 위해서는 다음의 import가 추가적으로 필요합니다.
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
BufferedReader는 Scanner와 다르게 IOException 처리를 해줘야 합니다.
- readLine()을 할 때마다 try & catch문을 사용하여 예외 처리
- throws IOException 처리
그리고 다음과 같은 방식으로 BufferReader를 선언하고, reaLine()을 통해 입력을 받아오면 됩니다.
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
String str = br.readLine();
BufferedReader는 매개변수로 InputStreamReader를 사용하여 객체를 생성합니다.
InputStreamReader 란❓
: 문자 기반의 보조 스트림으로써 바이트 기반 스트림을 문자 기반 스트림으로 연결시켜 주는 역할을 합니다.
BufferedWriter란 ❓
일반적으로 출력을 할 때, System.out.println("") 나 System.out.print()를 사용합니다.
대부분 사람들이 학부생때부터 사용을 했기 때문에 익숙하고, 적은 양의 출력에서는 편리하고, 그렇게 큰 성능 차이 없이 사용할 수 있습니다.
하지만 많은 양의 데이터를 출력을 할 때는 입력과 동일하게 버퍼(Buffer)를 사용하는 것이 좋습니다.
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out)); // 선언
String str = "abcdef";
BufferedWriter와 함께 사용할 수 있는 메소드는 다음과 같습니다.
Type | 설명 |
void | close() 스트림을 닫습니다. 닫기 전 flushing() 필수. |
void | flush() 스트림을 비움 |
void | newLine() 개행 문자 역할 |
void | write(char[] buf, int offset, int length ) 버퍼 offset 위치부터 length 크기 만큼 write |
void | write(int c) 한 글자 쓰기 |
void | write(String s, int offset, int length) 문자열에서 offset에서부터 일정 길이만큼 write |
import java.io.*;
public class BufferWriterTest {
public static void main(String[] args) throws IOException {
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
bw.write("hello\n");
bw.newLine();
bw.write("I am writing\n");
bw.flush();
bw.close();
}
}
BufferedWriter 메소드를 사용한 예제입니다.
StringBuilder란 ❓
String str1 = "Hello ";
String str2 = "Java";
str1 += str2;
System.out.println(str1); //"Hello Java"
String은 불변(immutable) 객체이기 때문에 한 번 생성하면 변경할 수 없습니다. 따라서 String의 str1와 str2를 + 연산을 하게 되면 "Hello java"라는 새로운 String을 생성하게 됩니다.
즉 원래의 str1은 JVM의 Garbage Collector가 처리를 합니다.
새로운 객체를 계속해서 생성한다는 말은 메모리 할당과 메모리 해제를 계속해서 발생시킨다는 의미이기 때문에 연산이 많아지게 되면 성능적으로 좋지 않습니다.
이러한 문제를 해결하기 위해서 나온 클래스가 바로 "StringBuilder"입니다.
StringBuilder 란 ❓
StringBuilder 클래스는 버퍼(Buffer)를 사용하여 문자열을 문자 시퀀스(CharSequence)로 관리합니다.
Buffer가 가득 차게 되면 버퍼의 크기가 두 배로 변경됩니다.
StringBuilder는 String과 문자열을 더할 때 객체를 생성하는 것이 아니라 기존의 데이터에 추가하는 방식을 사용하기 때문에 속도도 빠르며 String에 비해서 부하도 적습니다.
StringTokenizer란 ❓
아까 BufferBuilder 클래스에서 데이터 입력 시 'Enter'를 통해 구분된다고 했습니다.
이는 입력이 한 줄씩 이어졌을 때고, 공백 단위나 지정한 구분자로 문자열을 나누어 줘야 할 때는 따로 나눠 줄 필요가 있습니다.
StringTokenizer 클래스는 문자열을 우리가 지정한 구분자로 문자열을 쪼개 주는 클래스입니다.
지정한 구분자로 쪼개어진 문자열을 토큰(Token)이라고 부르며, StringTokenizer 클래스를 사용하기 위해서는 다음의 import가 필요합니다.
java.util.StringTokenizer;
countTokens()
: 전체 token의 개수가 아닌 남아있는 token의 개수(int)를 반환합니다.
hasMoreElements() & hasMoreTokens()
: 현재 위치 뒤에 있는 문자열에서 하나 이상의 토큰을 사용할 수 있는 경우 true 사용하지 못한다면 false를 반환합니다.
nextElement() & nextToken()
: 다음 존재하는 token을 반환합니다. nextElement()는 Object 타입, nextToken()은 String 타입을 반환합니다.
Scanner 클래스와 BufferedReader 클래스 비교
두 정수 A와 B를 입력받은 다음, A+B를 출력하는 프로그램을 작성하는 간단한 문제입니다. 근데 프로그램이 언제 종료되는지 안 알려주는 EoF 문제입니다.
Scanner 클래스
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
while (sc.hasNextInt()){
int A = sc.nextInt();
int B = sc.nextInt();
System.out.println(A+B);
}
}
}
Scanner 클래스와 System.in을 통해 Scanner 객체를 생성합니다.
Scanner 클래스의 입력값이 int값 일 때만 true를 반환하는 hasnextInt() 메서드를 사용하여 값이 안 들어오면 종료되는 while문을 만들어 줬습니다.
System.in 이란 ❓ : 사용자로부터 입력을 받기 위한 입력 스트림
BufferedReader 클래스
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.StringTokenizer;
public class Main {
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
StringBuilder sb = new StringBuilder();
StringTokenizer token ;
String str ;
while ((str = br.readLine()) != null){
token = new StringTokenizer(str," "); // 분리
int A = Integer.parseInt(token.nextToken());
int B = Integer.parseInt(token.nextToken());
sb.append(A+B).append("\n");
}
System.out.println(sb);
}
}
BufferedReader를 생성해 주고, 매개변수로 InputStreamReader를 사용하여 객체를 생성해 줍니다.
계속해서 말했듯이 BufferedReader는 readLine()을 통해 한 행을 전부 읽기 때문에, 공백단위로 분리하기 위하여 StringTokenizer 클래스를 사용하여 공백을 기준으로 분리를 해줘야 합니다.
그리고 nextToken()은 String타입으로 반환을 하기 때문에, Integer.parseInt()로 캐스팅을 해줘야 합니다.
마지막으로 StringBuilder 클래스의 append() 메서드를 사용하여 문자열을 계속 추가해 주고 '\n'을 추가해 줘서 마무리해주었습니다.
위 그림을 보면 메모리도 적게 사용하고, 속도도 빠른 코드가 바로 "BufferedReader 클래스"입니다.
'Language > Java' 카테고리의 다른 글
[JAVA] Stream API에 대해 알아보기 _ Stream 생성 (2/5) (0) | 2024.05.16 |
---|---|
[JAVA] Stream API에 대해 알아보기 (1/5) (0) | 2024.05.15 |
[JAVA] 큰 수 다루기 (BigInteger, BigDecimal) (0) | 2024.05.13 |
[JAVA] char에서 String으로 변환하기 (value of() , charAt()) (0) | 2024.05.12 |
[JAVA] 형 변환 (캐스팅, casting) 알아보기 (0) | 2024.04.29 |