자바를 배우면서 다른 언어들과 다르게 입출력을 위해서 알아야한 내용이 많고 빠른 입출력을 구현하기 위해서는 복잡하고 많은 코드를 사용해야하기 때문에 이를 정리를 하고자 한다.
자바 입출력 (I/O : Input/Output)
I/O란 Input과 Output의 약자로 입력과 출력, 간단히 줄여서 입출력이라고 한다. 입출력은 컴퓨터 내부 또는 외부의 장치와 프로그램간의 데이터를 주고받는 것을 말한다.
스트림 (Stream)
스트림이란 데이터를 운반하는데 사용되는 연결통로이다. 자바에서 입출력을 수행하려면, 즉 어느 한쪽에서 다른 쪽으로 데이터를 전단하려면, 두 대상을 연결하고 데이터를 전송할 수 있는 무언가가 필요한데 이것을 스트림(Stream)이라고 정의한다. 스트림은 단방향통신만 가능하기 떄문에 하나의 스트림으로 입력과 출력을 동시에 처리할 수 없다. 그래서 입력과 출력을 동시에 수행하려면 입력을 위한 입력스트림(Input Stream)과 출력을 위한 출력스트림(Output Stream), 두 개의 스트림이 필요하다.
스트림의 종류
- 바이트기반 스트림 (InputStream, OutputStream)
스트림은 기본적으로 1Byte 단위로 데이터를 전송하며 입출력 대상에 따라 다음과 같은 입출력 스트림이 있다. 스트림은 기본적으로 InputStream, OutputStream 클래스를 가지고 있으며 모든 바이트기반 스트림의 조상클래스이다.
입력스트림 | 출력스트림 | 입출력 대상의 종류 |
FileInputStream | FileOutputStream | 파일 |
ByteArrayInputStream | ByteArrayOutputStream | 메모리 |
PipedInputStream | PipedOutputStream | 프로세스 |
... | ... | ... |
- 보조 스트림
보조 스트림은 말 그대로 다른 스트림의 기능을 보완하기 만들어진 스트림이다. 보조스트림은 실제 데이터를 주고 받는 스트림이 아니기 때문에 데이터를 입출력할 수 있는 기능은 없지만 스트림의 기능을 향상시키거나 새로운 기능을 추가할 수 있다. 그래서 보조스트림만으로 입출력을 처리할 수 없고 스트림을 먼저 생성한 다음에 이를 이용해야한다.
입력보조스트림 | 출력보조스트림 | 설명 |
BufferedInputStream | BufferedOutputStream | 버퍼를 이용한 입출력 성능 향상 |
DataInputStream | DataOutputStream | int, float와 같은 기본형 단위(primitive type)로 데이터를 처리하는 기능 |
ObjectInputStream | ObjectOutputStream | 데이터를 객체단위로 읽고 쓰는데 사용, 주로 파일을 이용하며 객체 직렬화와 관련있음 |
... | ... | ... |
// 기반 스트림 생성
FileInputStream fis = new FileInputStream("test.txt");
// 기반 스트림을 이용한 보조 스트림 생성'
BufferedInputStream bis = new BufferedInputStream(fis);
// 데이터 읽기
bis.read();
코드 상으로는 보조스트림인 BufferedInputStream이 입력 기능을 수행하는 것처럼 보이지만, 실제 입력 기능은 BufferedInputStream과 연결된 FileInputStream이 수행하고, 보조 스트림인 BufferedInputStream은 버퍼만을 제공한다. 버퍼를 사용한 입출력과 사용하지 않은 입출력간의 성능차이는 상당하기 때문에 대부분의 경우에 버퍼를 이용한 보조 스트림을 사용한다.
- 문자기반 스트림 (Reader, Writer)
입출력 단위가 1Byte인 바이트기반 스트림과 달리 입출력 단위를 문자를 의미하는 char형 2Byte로 하는 스트림을 문자기반 스트림이라고 한다. 자바에서는 문자 단위를 2Byte로 하기 때문에 입출력 단위를 1Byte로 하는 바이트기반 스트림으로 처리하기에는 어려움이 있다.
문자기반 스트림은 바이트기반 스트림에서 inputStream을 Reader로 outputStream을 Writer로 바꿔 사용하면 된다.
바이트기반 스트림 | 문자기반 스트림 |
FileInputStream FileOutputStream |
FileReader FileWriter |
... | ... |
바이트기반 보조스트림 | 문자기반 보조스트림 |
BufferedInputStream BufferedOutputStream |
BufferedReader BufferedWriter |
... | ... |
BufferedReader와 BufferedWriter
BufferedReader/BufferedWriter는 버퍼를 이용해서 입출력의 효율을 높일 수 있도록 해주는 역할을 한다. 버퍼를 이용하면 입출력의 효율이 비교할 수 잆을 정도로 좋아지기 때문에 사용하는 것이 좋다.
BufferedReader의 readLine()을 사용하면 데이터를 라인단위로 읽을 수 있고 BufferedWriter는 newLine()이라는 줄바꿈을 해주는 메소드를 가지고 있다.
InputStreamReader와 OutputStreamWriter
InputSteramReader/OutputSteramReader는 이름에서 알 수 있는 것과 같이 바이트기반 스트림을 문자기반 스트림으로 연결시켜주는 역할을 한다. 그리고 바이트기반 스트림의 데이터를 지정된 인코딩(OS에서 사용하는 기본 인코딩)의 문자데이터로 변환하는 작업을 수행한다.
표준입출력 (System.in, System.out, System.err)
표준입출력은 콘솔을 통한 데이터 입력과 콘솔로의 데이터 출력을 의미한다. 자바에서는 표준입출력(Standard I/O)을 위해 3가지 입출력 스트림, System.in, System.out, System.err를 제공하는데, 이들은 자바 어플리케이션 실행과 동시에 사용할 수 있게 자동적을 생성되기 때문에 개발자가 별도로 스트림을 생성하는 코드를 작성하지 않아도 사용이 가능하다.
- System.in 콘솔로부터 데이터를 입력받는데 사용 ← InputStream
- System.out 콘솔로 데이터를 출력하는데 사용 → OutputStream
- System.err 콘솔로 에러내용을 출력하는데 사용 → OutputStream
System.out 출력
System.out.print("HelloWorld");
System.out.printf("HelloWorld");
System.out.println("HelloWorld");
BufferedReader, InputStreamReader, System.in의 사용
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
Java.util.Scanner 클래스
Scanner는 화면, 파일, 문자열과 같은 입력소스로 부터 문자데이터를 읽어오는데 도움을 줄 목적으로 JDK1.5부터 추가되었다. Scanner에는 여러 종류의 매개변수를 가지는 생성자를 지원하기 때문에 다양한 입력소스로부터 데이터를 읽을 수 있다. 하지만 입력 처리 속도가 중요하고 대량의 입력 데이터를 다룰 때는 BufferedReader와 InputStreamReader를 사용하는 것이 더 좋다.
// BufferedReader, InputStreamReader
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
String str = br.readline();
// Scanner
Scanner sc = new scanner(System.in);
String str = sc.nextLine();
Java.util.StringTokenizer 클래스
StringTokenizer는 긴 문자열을 지정된 구분자(delimeter)를 기준으로 토큰(token)이라는 여러 개의 문자열로 잘라내는 데 사용된다. StringTokenizer 외에도 String 클래스의 split, BufferedReader 클래스의 split, Scanner의 useDelimeter가 있지만 입력 처리 속도가 중요하고 긴 문자열을 다룰 때에는 StringTokenizer를 쓰는 것이 좋다.
// StringTokenizer
StringTokenizer st = new StringTokenizer(br.readline());
// StringTokenizer에서 Delimiter 이용
// Delimiter의 Default는 " " 이지만, 써주는 것이 더 빠르다.
StringTokenizer st = new StringTokenizer(br.readline(), " ");
StringBuilder 클래스
String 클래스는 인스턴스를 생성할 때, 지정된 문자열을 변경할 수 없지만 StringBuilder 클래스는 String Constant Pool을 쓰지 않고 버퍼(Buffer)를 활용하여 문자열을 문자 시퀀스(CharSequence)로 관리하기 때문에 변경이 가능하다.
수정할 문자열의 길이를 고려하여 버퍼의 길이는 여유있게 충분히 잡아주는 것이 좋다. 편집 중인 문자열이 버퍼의 길이를 넘어서게 되면 버퍼의 길이를 늘려주는 작업이 추가로 수행되어야하기 때문에 작업효율이 떨어진다. StringBuilder의 버퍼가 가득 차면 버퍼의 크기가 (기존 용량 + 1)의 두 배로 변경된다.
// String
String str = "Hello";
// 새로운 인스턴스 생성됨
str += " World";
// StringBuilder 예시
StringBuilder stringBuilder = new StringBuilder("Hello");
// 기존 인스턴스에서 수정
stringBuilder.append(" World");
위 코드에서 String객체로 생성했을 때, String Constant Pool에 "Hello", "World", "HelloWorld"가 존재하고 Stringbuilder로 생성했을 때에는 버퍼에 "HelloWorld"만 존재한다.메모리, 실행시간 관리 측면에서는 Stringbuilder가 더 효율적이라고 할 수 있다.
// 100,000 번의 HelloWorld 출력
// 1.
System.out.println("HelloWorld");
System.out.println("HelloWorld");
System.out.println("HelloWorld");
...
System.out.println("HelloWorld");
System.out.println("HelloWorld");
System.out.println("HelloWorld");
// 2.
StringBuilder sb = new StringBuilder();
sb.append("HelloWorld");
sb.append("HelloWorld");
sb.append("HelloWorld");
...
sb.append("HelloWorld");
sb.append("HelloWorld");
sb.append("HelloWorld");
System.out.println(sb);
위 코드에서 StringBuilder를 사용하지 않을 때 System.out.println을 100,000번 PrintStream을 호출하여 출력하고, StringBuilder를 사용할 때에는 100,000번 길어지는 문자열을 버퍼에 보관해두고 PrintStream을 한번만 호출하여 출력하기 때문에 각 시간에 필요한 출력을 요구하지않는 이상 I/O호출 횟수가 더 적은 Stringbuilder를 이용하는 것이 더 효율적이다.
StringBuilder와 BufferedWriter 차이
StringBuilder는 문자열 조작(연결, 수정 등)을 빠르게 수행하기 위한 클래스이고, BufferedWriter는 출력 작업의 빈도를 줄여 데이터를 효율적으로 대량의 데이터를 출력하기 위한 클래스이다. 즉, 두 클래스 모두 내부적으로 버퍼를 이용하여 성능을 향상시키지만, 그 목적과 사용 방식은 다르다. StringBuilder는 문자열 조작 작업을 위한 버퍼를 이용하고, BufferedWriter는 출력 작업의 성능을 향상시키기 위해 버퍼를 이용한다.
'JAVA' 카테고리의 다른 글
[JAVA/자바] Code Convention 코드 컨벤션 (0) | 2024.02.28 |
---|---|
[JAVA/자바] 소수점 출력 (0) | 2024.02.25 |
[JAVA/자바] 실행시간 확인 (0) | 2023.10.03 |
[JAVA/자바] 메모리 사용량 확인 (0) | 2023.10.03 |
[JAVA/자바] Comparable과 Comparator (0) | 2023.09.26 |