Coding Test

Scanner/StringBuilder/ BufferedReader/Writer / StringTokenizer / 입출력정리

Backcoder 2022. 7. 29. 14:14


[Scanner]

Scanner sc = new Scanner(System.in);


sc.next => String 단어기준(띄어쓰기 구분)
sc.nextLine => 줄단위
sc.nextInt => int 만 뽑기
=> 기본이 띄어쓰기 단위 구분해서 입력값을 받기 때문에, 만능이다.

입력 :
5 3
for ( 5 번동안 )
5 4 3 2 1
for ( 3 번동안 )
1 3
2 4
5 5

이런 식의 입력을 받아야 할때도
Scanner 하나 생성해뒀으면 알아서 척척 next 값을 받아서 집어넣는다.

- 그럼 뭐가 고민인가 Scanner 쓰면 되지않나
라고 하기에는 BufferedReader 와의 메모리, 속도 등 성능차이가 꽤 벌어진다.
되도록 BufferedReader 를 사용해야 한다.


[ BufferedReader ]

BufferedReader br = new BufferedReader(new InputStreamReader(System.in));


- BufferedReader 는 Scanner 와 마찬가지로 처음에 한번만 생성해두고 사용하면 된다.

read => int, 즉 char단위용
( 스캐너와 헤깔려서 단어단위로 생각하고 쓰면 안된다)
readLine => 줄단위
( 스캐너처럼 단어단위가 없다.
=> StringTokenizer 구분자와 같이 사용한다 )

[ StringTokenizer ]

StringTokenizer tk1 = new StringTokenizer(br.readLine());


- 기본 default 값이 한칸 띄어쓰기 구분자
- StringTokenizer는 한번 사용되고 ( 엔터키를 입력했으면 한번 사용한거로 간주 )
다른 곳에서 또 필요할때는
다시 new 토크나이저 생성해서 사용한다.

반복문 구간에서
(1)Tokenizer 를 반복문 "바깥"에서 생성해서 사용

- 입력값이 엔터안치고 한줄에서만 토큰으로 들어올때

(2)Tokenizer 를 반복문 "안쪽"에서 생성해서 사용

- 띄어쓰기등 토큰 입력값이 엔터를 치면서 반복해서 들어올때


(예시)
입력값이 다음과 같을 때
1. int n과 int m이 띄어쓰기로 입력된다.
입력 : 5 3 <= StringTokenizer1 사용
int n = tk1.nextToken
int m = tk1.nextToken

2. 띄어쓰기로 n번동안 숫자 입력된다.

입력 : 5 4 3 2 1

StringTokenizer tk2 = new StringTokenizer 반복문 바깥 생성
for (n번동안) { tk2.nextToken }
( 한줄만 사용하는 거면 반복문 바깥 )

3. 띄어쓰기로 2개씩 m번동안 숫자 입력된다.
<세번 반복해서 tk 사용하는 상황>

입력 :
1 3
2 4
5 5

for ( m번동안 ){
StringTokenizer tk3 = new StringTokenizer 반복문 안쪽 생성
tk3.nextToken
}
( 여러줄 사용하는거면 반복문 안쪽 )

기본 사용법은 이렇다.
Scanner에 비해 불편하지만
익숙해지면 쓸만 하고,
어려운 로직 오랜시간 짜둔 걸 입출력 방법만 바꿔서 성능을 향상시킬 수 있다면
충분히 가치가 있다고 생각한다.
익숙해지자.


[ StringBuilder ]

StringBuilder sb = new StringBuilder();


- 입출력 속도향상의 연장선으로 StringBuilder 도 매우 유용하게 사용된다.

System.out.println( ); 은 사실 메모리나 속도면에서 좋은 성능을 보여주지 못한다.

한 두번 사용되는거야 아무문제 없지만
반복문안에서 System.out.println( ); 를 써서
10000번동안 사용된다면 이건 좀 성능을 떨어트릴만 하다.

이럴 때 사용할 수 있는게 StringBuiler 다.

StringBuilder sb = new StringBuilder();

for ( int i =0; i<100; i++ ){

    sb.append(i).append("\n");


}
System.out.println(sb);


StringBuilder 생성하고 사용하면 되는데, 얘는
String a = a + " 추가 " ;
이렇게 + 를 사용하는 대신
"append" 를 사용한다.
문장을 붙이고 붙이고 붙이며 말그대로 String을 Build 한다.

sb.append("안녕").append("하세요").append(", 저는").append(" 백코더입니다").append("\n");
=> 안녕하세요, 저는 백코더입니다.
( 줄구분이 없으므로 \n 을 적절히 사용해줘야 한다. )

이렇게 StringBuilder에 저장해두고,
System.out.println( sb ); 출력은 한번만 해주면 된다.


---

이 StringBuilder + System.out.println 방법을 사용하는게
=> BufferedWriter 다.

BufferedWriter 가 print 보다 성능이 좋다.


[ BufferedWriter ]

BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));


for ( int i =0; i<100; i++){
bw.write( i + "\n" ); }
// 이 write 기능이 StirngBuilder 처럼 출력할 값을 "저장" 해 놓는 것

bw.flush();
// 이 flush 가 System.out.println 처럼 출력하는 역할

bw.close(); //스트림을 닫음

- BufferedWriter 의 경우 버퍼를 잡아 놓았기 때문에 반드시
close() 를 호출해서 뒷 처리를 해주어야 한다.


write ( ) 할 때, => String / int 타입 받을 수 있는데,
int 타입으로 인식되면 오류가 나는 경우가 있으니, (컴파일 / text 가 깨져서 출력)
이럴 땐 String 타입으로 주면 잘 나온다. 뒤에 ( + "" ) 공백붙여주면 String으로 인식.




이렇게, 가장 편하고 기본적인
Scanner, System.out.println 부터해서

성능을 향상 시켜줄 꼼수방법 StringBuilder

성능좋게 태어난 Buffered 가문의
BufferedReader +StringTokenizer와, BufferedWriter 까지.

이 외에도 정말 많은 입출력 방법이 있지만
일단 기본적으로 이 정도는 편하게 사용할 수 있게끔 해야겠다.

사실 처음엔

"아니 콘솔에 뽑히는거 보니까 다 1초컷이구만 뭐가 더 빠르다는거야
그냥 좀 0.몇초 더 기다리면 안되나?"

생각이 들었는데,
근데 이건 숫자가 적은범위 입력이니까 1초컷일거고,
숫자가 많아지면 많아질수록 차이가 벌어지겠구나.
그게 서비스로 가서 0.5초 차이를 낸다면
당장 내가 사용할 때도, 0.5초 씩 늦은 서비스라면 사용하기 답답하지 않을까.

그 조금의 속도, 성능차이에 대한 생각을 다시 하게 됬다.