Practice makes perfect

[JAVA] 스트림(Stream) 본문

빅데이터/JAVA

[JAVA] 스트림(Stream)

kerpect 2020. 5. 20. 22:11

 

스트림(Stream)이란?

: 프로그램을 연결해주어 입출력을 가능하게 하는 통로라고 할 수 있습니다.

 

● 스트림 입출력

- 버퍼를 가지고 순차적으로 이루어지는 입출력을 의미합니다.

   자바의 입출력 스트림

- 입력 스트림 : 입력 장치로부터 자바 프로그램으로 데이터를 전달합니다.

- 출력 스트림 : 출력 장치로 데이터 출력합니다.

 

 

● 자바의 입출력 스트림 특징

 

- 스트림의 양끝에 입출력장치와 자바 응용프로그램 연결 합니다.

- 스트림은 단방향 : 입력과 출력을 동시에 하는 스트림은 없습니다.

- 입출력 스트림 기본 단위 : 바이트 스트림의 경우 : 바이트 , 문자 스트림의 경우 : 문자(자바에서는 문자1: 2 바이트)

- 선입선출 구조로 되어있습니다.

 

입력 스트림 : InputStream - 프로그램으로 데이터를 읽어드리는 스트림입니다.

출력 스트림 : OutputStream - 프로그램으로 데이터를 내보내는 스트림입니다.

InputStream in= new FileInputStream("Fonts.zip");

스트림의 생성은 결국 인스턴스의 생성으로 가능합니다.

 

 

 

1) FileStream

: 파일을 대상으로 프로그램과 연결해주어 입출력을 가능하게 하는 통로의 역할을 합니다.

public class ByteFileCopy {
	public static void main(String[] args) {
	
		try { 
			int copyByte = 0;
			InputStream in= new FileInputStream("Fonts.zip");// 입력용 stream 
			OutputStream out = new FileOutputStream("Copy.zip"); // 출력용 stream 
			
		    while(true) {
			    int bData = in.read(); 
			    if(bData == -1) { 
				    break; 
			    }
			    out.write(bData);  
			    copyByte++;
		    }
		    
		    in.close();
		    out.close();
		   
		    System.out.println("복사된 바이트 크기 : " + copyByte );

		} catch (IOException e) { 
			e.printStackTrace();
		} 

- InputStream, OutputStream 모든 입출력의 최상위 클래스로  FileInputStream, FileOutputStream이  상속하기 때문에 다형성에 의해서 부모class를 사용해도 문제되지 않는다. 또한 오버라이딩에 의해서 메소드를 호출 시킵니다.

 

- 입력의 대상에 적절하게 read 메소드가 정의되어 있습니다. 단 read(); byte 단위로 읽어오는데 반환형을 int를 사용합니다.  모든 내용을 읽어오면 - 1이라는 값을 return 해줍니다.

 

- out.write(bData); // 1byte 로 읽어서 처리 하기 때문에 많은 시간이 소요됩니다.

 

- close : Stream 을 통해서 할당 받았던 메모리에 저장했었던 자료들을 한번에 정리하여 재사용 할 수 있도록 해줍니다.

 모든 코드를 사용한 이후에는 close를 반드시 해줘야합니다.

 

- (IOException e)예외가 발생하는 경우 : 관리자 권한으로 연결되어지는 file 같은 경우(운영체제의 권한에 의해서)에 발생할 수 있습니다.

 

주의 )

위 예제에서는 바이트 단위 복사(1바이트씩 복사)가 진행된다. 따라서 50MB가 넘는 크기의 파일 복사에는 오랜 시간이 걸립니다.

 

위의 문제점인 시간이 오래 걸리는 것을 해결하기 위한 방법이 을 보여드리겠습니다.

public class BufferFileCopy {
	public static void main(String[] args) {
		
		try { 
			byte[] buf = new byte[1024]; // 1024 byte = 1kb (1kb씩 저장하도록 만듬)
			int copyByte = 0;
			InputStream in= new FileInputStream("Fonts.zip");
			OutputStream out = new FileOutputStream("CopyBuffer.zip");
			
		    while(true) {
			    int readLen = in.read(buf); 
			   
			    if(readLen == -1) { 
				    break; 
			    }
			    out.write(buf, 0, readLen);  
                // buf 배열(크기) - 1024개  ,  0번째부터 , readLen 의 개수까지 write
			    copyByte+=readLen;  // 전체읽어온 개수 확인 
		    }
		    in.close();
		    out.close();
		    
		    System.out.println("복사된 바이트 크기 : " + copyByte );

		} catch (IOException e) { 
          e.printStackTrace();} 

배열을 선언byte[] buf = new byte[1024]; 1024 byte = 1kb (1kb씩 저장하도록 만듬) 합니다.

선언한 배열을 read(); 에 넣어서 아무것도 넣지 않았을 때 1byte 씩 읽어오지만 buf을 넣어주면 읽어온 데이터를 1024개까지 쌓아서 buf가 꽉차면 읽어온 data의 개수 만큼 return 해줍니다. 이것을 통해 시간을 많이 단축할 수 있습니다

 

 

 

2) DataStream

: 자바의 기본 자료형 데이터를 바이트 스트림으로 입출력하는 기능을 제공합니다.

 

- DataInputStream과 DataOutputStream 은 FilterInputStream과 FilterOutputStream을 상속하고 있어, 객체 생성시에 InputStream과 OutputStream을 매개변수 인자를 가집니다.

이 클래스와 입출력 장치를 대상으로 하는 입출력 클래스를 같이 이용하면 자바의 기본 자료형 데이터를 파일 등 입출력 장치로 직접 입출력할 수 있습니다.

 

public class DataFilterStream {
	public static void main(String[] args) {
		
		 try {
			OutputStream out = new FileOutputStream("data.bin"); 
			// file 이 없으면 입력한 이름으로 file 을 만들어준다. 
            // file이 이미 존재하면 원래 있던 내용을 다 지우고 내용을 입력한다.
			       
			DataOutputStream filterOut = new DataOutputStream(out);
			// OutputStream을 연결. 
			
			// out.write(365); // 별도의 자료형 입력이 없을 때는 int로 저장된다. 
			// out.close();
			
			filterOut.writeInt(365); // : 정수값 저장
			filterOut.writeDouble(3.14); // : 실수값 저장 
			filterOut.close(); // 필터를 close 하면 연결된 Stream 도 close 된다. 
			
			InputStream in = new FileInputStream("data.bin");
			DataInputStream filterIn = new DataInputStream(in);
			// InputStream 연결. 
			
			System.out.println(filterIn.readInt());
			System.out.println(filterIn.readDouble());
			
			filterIn.close();
			
					
		} catch (IOException e) {
			e.printStackTrace();
		}

 

FileInputStream / FileOutputStream 은 byte[] 단위의 데이터만 입/출력을 할 수 있었습니다. 하지만 DataStream Filter를 적용함으로써, 자바 기본 자료형(char, int, long, ...) 으로 데이터를 입력하고 출력할 수 있게 되었습니다.

 

 

 

FileStream은 1 Byte 단위로 입/출력이 이루어지면 기계적인 동작이 많아지므로 효율이 떨어지게 됩니다. 또한, 사용자가 일일이 버퍼와 크기를 지정하여 입출력을 하게 되는 것도 정적이고 불편합니다. 이러한 문제점을 해결하기 위해서 사용하는 것이 BufferedStream 입니다.

 

3) BufferedStream

public class ByteBufferedFileCopy {
	public static void main(String[] args) {
		
		// 퍼포먼스의 문제를 최소화 하여 결과값을 가질 수 있도록 한다. 
		try { 
			int copyByte = 0;
			InputStream in= new FileInputStream("Fonts.zip");
			BufferedInputStream bufferIn =  new BufferedInputStream(in);
			
			OutputStream out = new FileOutputStream("Copy1.zip");
			BufferedOutputStream bufferOut = new BufferedOutputStream(out);
	
		    while(true) {
			    int bData = bufferIn.read(); 
	
			    if(bData == -1) { 
				    break; 
			    }
			    bufferOut.write(bData); 
			    copyByte+= bData;
		    }
		    bufferIn.close();
		    bufferOut.close();
		    
		    System.out.println("복사된 바이트 크기 : " + copyByte );

		} catch (IOException e) { 
			e.printStackTrace();
		} 

사용자가 BufferedInputStream과 BufferedOutputStream을 이용하여 프로그램을 작성하면 1 바이트씩 읽고 쓰는 모든 작업이 하드 디스크 파일이 아닌 내부적인 버퍼를 대상으로 발생하며, 필요에 따라 버퍼와 하드 디스크 파일간에 입출력이 간헐적으로 발생하므로 전체적인 입출력 성능이 동적으로 향상되도록 해줍니다.

 

 

4) FileWriter
: 운영체제의 포멧으로 데이터를 저장하도록 합니다.

public class FileWriterStream {   
	public static void main(String[] args) { 

		char ch1 = 'A';
		char ch2 = 'B';
		// 윈도우 저장할 때,  영어는 1byte 로 처리, 
        // 한국어는 2byte 다른 나라는 3byte로 처리되는 곳도 있다. 
		
		try {
			Writer out = new FileWriter("hyper.txt"); 
			out.write(ch1);                                       
			out.write(ch2);                                    
			out.close();                                        
			
		} catch (IOException e) {
			e.printStackTrace();
		} 

 

FileWriter 운영체제가 관리하는 문자열 포멧으로 운영체제가 파일에 담게 처리합니다. 운영체제가 저장하고 있기 때문에 깨지지 않고 저장됩니다. char 자료형 2개 이므로 2byte 로 저장합니다. 메모장으로 읽을 때는 깨지지 않고 잘 보이지만 java에서 그대로 읽어내면 깨져서 보입니다.

java의 형식으로 저장되는 것이 아닙니다. 만약 java의 형태였다면 4byte로 저장되고, 깨져보였을 것입니다.

 

5) FileReader

: 운영체제의 포멧에 맞춰서 저장했던 데이터를 java의 문자처리 포멧으로 변환해서 반환해줍니다. 

public class FileReaderStream { 
	public static void main(String[] args) {
	
		char[] cbuf = new char[10];
		
		try { 
			Reader in = new FileReader("hyper.txt"); 
			int readCnt = in.read(cbuf, 0, cbuf.length); // 읽어온 개수만큼만 반환을 해준다. 
			for(int i=0; i < readCnt ; i++) {
				System.out.println(cbuf[i]);
			}
			in.close();
		} catch (IOException e) {
			e.printStackTrace();
		}

 

- JDK의 바이트 스트림 클래스 계층 구조

 

 

 

- JDK의 문자 스트림 클래스 계층 구조

 

'빅데이터 > JAVA' 카테고리의 다른 글

[JAVA] SWING(1)  (0) 2020.05.23
[JAVA] 네트워크(NETWORK)  (0) 2020.05.23
[JAVA] Thread  (0) 2020.05.19
[JAVA] 컬렉션 프레임워크 ( Collection Framework )  (0) 2020.05.18
[JAVA] Generics  (0) 2020.05.17