백준 2751 수 정렬하기 2 문제를 풀던 중, 100만개의 데이터를 정렬할 때, Collections.sort(list)에 문제점이 있나 확인하면서 찾았지만, 정렬이 문제가 아닌 데이터를 읽는 시간이 문제였다. Scanner로 읽으면 성능이 매우 떨어져서 시간초과가 났는데 BufferedReader로 데이터를 읽었을 때 성공했다. BufferedReader는 무엇인지 빠른 성능으로 입력을 받을 때 어떤 라이브러리를 사용하는지 확인해본다.
1.1 입출력이란?
I/O란 Input과 output의 약자로 입력과 출력, 간단히 입출력이라고 한다. 입출력은 컴퓨터 내부 또는 외부의 장치와 프로그램간의 데이터를 주고받는 것을 말한다.
1.2 스트림(Stream)
자바에서 입출력을 수행하려면, 즉 어느 한쪽에서 다른쪽으로 데이터를 전달하려면, 두 대상을 연결하고 데이터를 전송할 수 있는 무언가가 필요한데 이것을 스트림이라고 한다. 스트림이란 데이터를 운반하는데 사용되는 연결통로이다. 스트림은 단방향통신이기 때문에 하나의 스트림으로 입력과 출력을 동시에 처리할 수 없으며 입력 스트림(input stream)과 출력 스트림(output stream) 2가지가 필요하다. 스트림은 먼저 보낸 데이터를 먼저 받게 되어 있으며 중간에 건너뜀 없이 연속적으로 데이터를 주고 받는다.
1.3 바이트 기반 스트림
스트림은 바이트 단위로 데이터를 전송하며 InputStream / OutputStream 이 모든 입출력 스트림의 조상이다.
입력 스트림 | 출력 스트림 | 입출력 대상의 종류 |
FileInputStream | FileOutputStream | 파일 |
ByteArrayInputStream | ByteArrayOutputStream | 메모리(byte 배열) |
PipedInputStream | PipedOutputStream | 프로세스(프로세스 간 통신) |
AudioInputStream | AudioOutputStream | 오디오장치 |
InputStream | OutputStream |
abstract int read() | abstract void write(int b) |
읽기와 쓰기를 수행하는 메서드는 read()와 write() 메서드이며 입출력 대상에 따라 읽고 쓰는 방법이 다르기 때문에 각상황에 맞게 쓰라고 추상형으로 정의되어 있다.
1.4 보조 스트림
스트림의 기능을 보조하기 위해 보조스트림의 기능이 있다. 보조스트림은 실제 데이터를 주고받는 스트림이 아니기 때문에 데이터를 입출력을 처리할 수 없고 스트림을 먼저 생성한 다음에 이를 이용해서 보조스트림을 생성해야 한다.
FileInputStream 자손 | OutputStream 자손 |
BufferedInpuStream | BufferedOutputStream |
DataInputStream | DataOutputStream |
PushbackInputStream | PrintStream |
//기반 스트림 생성
FileInputStream fis = new FileInputStream("test.txt");
//보조스트림 생성
BufferedReader br = new BufferedReader(fis);
//보조스트림으로부터 데이터를 읽는다.
bis.read();
실제 수행은 FileInputStream이 하고 보조 스트림인 BufferedInputStream은 버퍼만 제공한다. 버퍼를 사용한 입출력과 사용하지 않는 입출력간에는 성능차이가 상당하기 때문에 보조 스트림을 쓰는 것을 추천한다.
1.5 문자 기반 스트림 / 보조 스트림
바이트 기반 스트림은 입출력의 단위가 1byte이다. C언어와 달리 Java는 한 문자를 의미하는 char형이 1byte가 아니라 2byte이다. 따라서 바이트기반의 스트림으로 2byte 문자열을 처리하는데는 어려움이 있기 때문에 byte[]를 char[]로 처리하는 문자 기반 스트림이 등장하였다. BufferedReader는 readLine()을 사용하여 데이터를 라인단위로 읽을 수 있고 BufferedWriter는 newLine()을 통해 줄바꿈을 해준다.
바이트기반 스트림 | 문자 기반 스트림 |
InputStream | Reader |
OutputStream | Writer |
특히, 텍스트 파일을 다루는 경우에 바이트 기반의 스트림인 FileInputStream / FileOutputStream을 사용하지 않고 문자기반 스트림인 FileReader / FileWriter을 사용해야 훨씬 성능이 좋다.
바이트 기반 스트림 | 문자 기반 스트림 | ||
InputStream | OutputStream | Reader | Writer |
FileInputStream | FileOutputStream | FileReader | FileWriter |
BufferedInputStream | BufferedOutputStream | BufferedReader | BufferedWriter |
특히 문자열을 입력 받을 때, 바이트 기반 스트림을 문자 기반 스트림으로 바꿀 수 있는데 InputStreamReader / OutputStreamWriter를 사용한다. 이름도 [바이트기반스트림 + 문자기반스트림] 조합으로 구성되어 있다.
문자열을 입력 받을 때
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
br.readLine()...
InputStreamReader(System.in)을 통해 입력을 읽고 BufferedReader 문자 기반 보조 스트림을 이용해 성능을 향상시킨다.
파일의 내용을 읽을 때
FileReader fr = new FileReader("text.txt");
BufferedReader br = new BufferedReader(fr);
br.readLine()..
FileReader를 통해 문서를 읽고 .BufferedReader 문자 기반 보조 스트림을 이용해 성능을 향상시킨다.
Scanner vs BufferedReader
Scanner와 BufferedReader 각각을 통한 60만개 데이터 추가 소요시간을 알아본다. 직접 입력하면 입력시간까지 추가되므로, 대신에 FileReader를 통해 1~600000까지의 숫자가 저장된 파일을 불러와서 추가한다.
Scanner 사용한 60만개 데이터 추가
import java.util.*;
import java.io.*;
public class Movie {
public static void main(String args[]) throws IOException {
FileReader fr = new FileReader("123.txt");
Scanner sc = new Scanner(fr);
ArrayList<Integer> arr = new ArrayList<Integer>();
long start = System.currentTimeMillis();
for (int i = 0; i < 600000; i++){
arr.add(sc.nextInt());
}
Collections.sort(arr);
long end = System.currentTimeMillis();
System.out.println( "실행 시간 : " + ( end - start )/1000.0 );
}
}
실행시간 : 0.645~0.704
BufferedReader 사용한 60만개 데이터 추가
import java.util.*;
import java.io.*;
public class Movie {
public static void main(String args[]) throws IOException {
FileReader fr = new FileReader("123.txt");
BufferedReader br = new BufferedReader(fr);
ArrayList<Integer> arr = new ArrayList<Integer>();
long start = System.currentTimeMillis();
for (int i = 0; i < 600000; i++){
arr.add(Integer.parseInt(br.readLine()));
}
Collections.sort(arr);
long end = System.currentTimeMillis();
System.out.println( "실행 시간 : " + ( end - start )/1000.0 );
}
}
실행시간 : 0.131 ~ 0.18
백준 2751 수 정렬하기 2
import java.util.*;
import java.io.*;
public class Main {
public static void main(String args[]) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
int inputcount = Integer.parseInt(br.readLine());
ArrayList<Integer> arr = new ArrayList<Integer>();
for (int i = 0; i < inputcount; i++){
arr.add(Integer.parseInt(br.readLine()));
}
Collections.sort(arr);
for (int i = 0; i < arr.size(); i++){
bw.write(arr.get(i)+"\n");
}
bw.close();
}
}
import java.util.*;
import java.io.*;
public class Main {
public static void main(String args[]) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
int inputcount = Integer.parseInt(br.readLine());
ArrayList<Integer> arr = new ArrayList<Integer>();
for (int i = 0; i < inputcount; i++){
arr.add(Integer.parseInt(br.readLine()));
}
Collections.sort(arr);
for (int i = 0; i < arr.size(); i++){
System.out.println(arr.get(i));
}
}
}
Scanner , System.print.out 2개 모두 사용하면 시간초과,
BufferedReader만 사용하면 4832ms
BufferedWriter만 사용하면 2816ms
BufferedReader,BufferedWriter 둘다 사용 시 1640ms 시간초를 소요한다.
(들어 온 모든 문자열에 대해 출력을 함로 BufferedWriter의 성능이 발휘된다. 그냥 하나만 출력 할 경우에는 System.out.print 써도 무방하다.)
백준 15552 빠른 A+B
import java.util.*;
import java.io.*;
public class Movie {
public static void main(String args[]) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
int num = Integer.parseInt(br.readLine());
for (int i = 0; i < num; i++) {
String[] line = br.readLine().split(" ");
bw.write(Integer.parseInt(line[0]) + Integer.parseInt(line[1]));
}
bw.close();
}
}
BufferedReader와 InputStreamReader를 통해 byte 기반의 스트림을 문자 기반 스트림으로 읽어들어오는 것은 이전에 했었다.
BufferedWriter와 OutputStreamWriter(System.out)은 System.out.println()대체하여 사용할 수 있다. bw.write()를 통해 출력할 문자열들을 버퍼에 추가하고, 마지막에 bw.close()를 하여 버퍼에 있는 내용들을 콘솔(화면)에 출력한다.
'Algorithm > 이론' 카테고리의 다른 글
다익스트라(dijkstra) (0) | 2020.07.13 |
---|---|
PriorityQueue / Deque (0) | 2020.07.10 |
Set<E> vs Map<K,V> (0) | 2020.07.10 |
Comparable & Comparator (0) | 2020.07.06 |
투 포인터(Two Pointer) (0) | 2020.07.05 |