Buffer

Programming/Java NIO 2015. 9. 8. 11:18

Java NIO 버퍼들은 NIO 채널들과 상호작용할 때 사용된다. 알다시피, 데이터는 채널로부터 버퍼에 읽어지고, 버퍼로부터 채널에 기록된다.


본질적으로 버퍼는 추후에도 다시 읽을 수 있는 데이터의 기록이 가능한 메모리 블럭이다. 이 메모리 블럭은 NIO 버퍼 객체로 래핑되어있고, 메모리 블럭의 사용을 더 쉽도록 메소드 집합을 제공한다.

Basic Buffer Usage


버퍼를 사용하여 데이터를 읽고 기록하는데 있어 일반적으로 다음 4단계의 작은 작업을 따른다:

  1. 버퍼에 데이터를 기록한다
  2. buffer.flip()를 호출한다
  3. 버퍼 바깥으로 데이터를 읽는다
  4. buffer.clear()나 buffer.compact()를 호출한다

버퍼에 데이터를 기록할 때, 버퍼는 데이터를 얼마나 기록했는지 추적한다. 데이터를 읽을 필요가 있을 때, 반드시 기록 모드에서 읽기 모드로 버퍼를 변경하기 위한 flip() 메소드를 호출해야 한다. 읽기 모드에서의 버퍼는 버퍼에 기록된 데이터 모두를 읽을 수 있게 해준다.


모든 데이터를 읽고나면, 다시 기록할 준비를 위해 버퍼를 삭제해야 한다. 두 가지 방법이 있다: clear()나 compact()를 호출한다. clear() 메소드는 전체 버퍼를 삭제한다. compact() 메소드는 이미 읽은 데이터만 삭제한다. 읽지 않은 데이터는 버퍼의 처음으로 옮겨지고, 데이터는 버퍼의 읽지 않은 뒤부터 기록될 것이다.


다음 write, flip, read, clear 작업에는 굵게 표시를 한 Buffer 사용의 간단한 예제이다:

RandomAccessFile aFile = new RandomAccessFile("data/nio-data.txt", "rw");
FileChannel inChannel = aFile.getChannel();

//create buffer with capacity of 48 bytes
ByteBuffer buf = ByteBuffer.allocate(48);

int bytesRead = inChannel.read(buf); //read into buffer.
while (bytesRead != -1) {

  buf.flip();  //make buffer ready for read

  while(buf.hasRemaining()){
      System.out.print((char) buf.get()); // read 1 byte at a time
  }

  buf.clear(); //make buffer ready for writing
  bytesRead = inChannel.read(buf);
}
aFile.close();

Buffer Capacity, Position and Limit


본질적으로 버퍼는 추후에도 다시 읽을 수 있는 데이터의 기록이 가능한 메모리 블럭이다. 이 메모리 블럭은 NIO 버퍼 객체로 래핑되어있고, 메모리 블럭의 사용을 더 쉽도록 메소드 집합을 제공한다.


Buffer 의 작업 방법을 알기 위해서 잘 알고있어야 할 세가지 Buffer 속성이 있다:

  • capacity
  • position
  • limit

position과 limit의 의미는 버퍼가 읽기모드인가 기록모드인가에 달려있다. 용량은 버퍼 모드와는 상관없이 항상 같다.


다음과 같이 읽기모드와 기록모드에서의 용량, 위치, 제한의 그림이 있다. 이에 대한 설명은 그림 뒤 섹션에 있다.

Java NIO: Buffer capacity, position and limit in write and read mode.

기록모드와 읽기모드에서의 버퍼 용량(capacity), 위치(position), 제한(limit)

Capacity


특정 고정된 크기를 지닌 Buffer의 메모리 블럭이 되는 것을 "용량"이라고도 부른다. 버퍼엔 오직 byte, longs, chars등을 capacity 만큼만 쓸 수 있다. 버퍼가 가득차게 되면, 버퍼에 더 많은 데이터를 쓸 수 있도록 하기 전에 그것을 비워야만(데이터를 읽거나 삭제) 한다.

Position


Buffer에 데이터를 기록할 때, 특정 위치에 수행한다. 기본 위치는 0이다. Buffer에 한 byte, long 등이 기록됐을 때, 데이터 삽입을 위해 위치는 다음 셀을 가르키도록 위치가 전진된다. 위치는 최대 capacity - 1만큼 가능하다.


버퍼로부터 데이터를 읽을 때, 주어진 위치로부터 수행한다. 기록모드에서 읽기모드로 Buffer를 flip 할 때, 위치는 0으로 초기화된다. 위치로부터 Buffer에서 데이터를 읽은 뒤, position은 읽기 위한 다음 위치로 전진된다.

Limit


기록모드에서 Buffer의 제한은 버퍼에 기록할 수 있는 데이터의 크기 제한이다. 기록모드에서 제한은 Buffer의 용량 크기와 같다. 


Buffer가 읽기모드로 flipping 할 때, 제한은 데이터로부터 얼만큼 읽을 수 있는지를 나타낸다. 그러므로 Buffer를 읽기모드로 flipping 할 때, 제한은 기록모드로의 기록 위치로 설정된다. 바꿔 말하면, 기록된 많은 바이트를 읽을 수 있다는 것이다(제한은 기록된 바이트의 숫자만큼 설정되고, 위치로 표시된다).

Buffer Types


Java NIO는 다음 Buffer 종류들을 제공한다:

  • ByteBuffer
  • MappedByteBuffer
  • CharBuffer
  • DoubleBuffer
  • FloatBuffer
  • IntBuffer
  • LongBuffer
  • ShortBuffer

볼 수 있듯이, 이러한 Buffer 종류들은 다른 데이터 종류들을 대표한다. 바꿔 말하면, 버퍼에 bytes와 더불어 char, short, int, long, float, double로 대신 작업 할 수 있다. 


MappedByteBuffer는 약간 특별하고, 그것은 자신만의 텍스트로 구성되어 있다.

Allocating a Buffer


Buffer 객체를 얻기 위해 가장 먼저 그것을 할당해야 한다. 모든 Buffer 클래스는 이를 할당하는데 allocate() 메소드를 갖고 있다. 다음 48바이트의 capacity를 갖는 ByteBuffer의 할당을 보여주는 예제이다:

ByteBuffer buf = ByteBuffer.allocate(48);

다음 1024 캐릭터의 공간을 갖는 CharBuffer의 할당을 보여주는 예제이다:

CharBuffer buf = CharBuffer.allocate(1024);

Writing Data to a Buffer


Buffer에 데이터를 기록할 수 있는 방법에는 두가지가 있다:

  1. Channel로부터 Buffer에 데이터를 기록한다
  2. 버퍼의 put() 메소드를 통해, Buffer에 직접 데이터를 기록한다

다음 Channel이 Buffer에 어떻게 데이터를 기록하는지 보여주는 예제이다:

int bytesRead = inChannel.read(buf); //read into buffer.

다음 put() 메소드를 통해 Buffer에 데이터를 기록하는 예제이다:

buf.put(127);    

다양한 방법으로 Buffer에 데이터를 쓸 수 있도록 다양한 버전의 put() 메소드가 있다. 예를 들어, 특정 위치에 기록하거나, 버퍼에 바이트 배열을 기록하는 것이 있다. 자세한 내용은 구체적인 버퍼 구현을 위한 JavaDoc을 참조하기 바란다. 

flip()


flip() 메소드는 Buffer를 기록모드에서 읽기모드로 변환한다. flip() 호출은 position을 0으로 되돌리고, limit을 위치가 있었던 곳으로 설정한다.


바꿔 말하면, 이제 position은 읽는 위치를 나타내고, limit은 버퍼에 bytes, chars 등이 얼마나 기록되어 있는지 나타낸다 - bytes, chars 등을 얼마나 읽을 수 있는지의 제한.

Reading Data from a Buffer


Buffer로부터 데이터를 읽는 방법에는 두가지가 있다.

  1. 버퍼로부터 채널 데이터를 읽는다.
  2. get() 메소드들 중 하나를 사용하여 버퍼로부터 직접 데이터를 읽는다.

다음 버퍼로부터 채널 데이터를 읽는 방법을 보여주는 예제이다:

//read from buffer into channel.
int bytesWritten = inChannel.write(buf);

다음 get() 메소드를 사용한 Buffer로부터 데이터를 읽는 예제이다:

byte aByte = buf.get();    

다양한 방법으로 Buffer로부터 데이터를 읽을 수 있도록 다양한 버전의 get()메소드가 있다. 예를 들어, 특정 위치를 읽거나, 버퍼로부터 바이트 배열을 읽는 것이 있다. 자세한 내용은 구체적인 버퍼 구현을 위한 JavaDoc을 참조하기 바란다.

rewind()


Buffer.rewind()는 위치를 0으로 되돌려 버퍼의 모든 데이터를 다시 읽을 수 있다. limit는 손대지 않은채 유지되므로, 여전히 Buffer로부터 읽을 수 있는 많은 요소들(bytes, chars 등)이 표시되어 있다.

clear() and compact()


Buffer에서 데이터를 꺼내 읽은 후엔 Buffer에 다시 기록하기 위한 준비를 해야한다. clear()compact()를 호출하여 수행할 수 있다.


만약 clear()를 호출하면 position은 0으로 되돌려지고 limit는 capacity로 설정된다. 바꿔 말하면, Buffer가 삭제된다. Buffer 내부의 데이터는 삭제되지 않는다. 오직 표시만이 Buffer의 데이터를 쓸 수 있는 곳이 어딘지를 말해준다.


만약 Buffer에 읽지 않은 데이터가 남겨진 채 clear()를 호출할 때, 데이터는 "잊혀질" 것이고, 이는 더이상 데이터 읽어진 것과 무엇을 읽을지 말해줄 표시가 없음을 의미한다.


만약 Buffer에 읽지 않은 데이터가 여전히 남아있고, 추후에 그것을 읽기 원한다면, 무언가를 기록하지 이전에, clear() 대신 compact()를 호출하도록 한다.


compact()는 Buffer의 시작부터 읽지 않은 데이터 모두를 복사한다. 그러면 position을 읽지 않은 마지막 요소 바로 다음으로 설정한다. clear()와 마찬가지로 limit 속성은 여전히 capacity로 설정되어 있다. 이제 Buffer는 기록 준비가 되었으나, 읽지 않은 데이터를 덮어쓰지는 않을 것이다.

mark() and reset()


Buffer.mark() 메소드를 호출하여 Buffer에 주어진 위치를 표시할 수 있다. 추후 Buffer.reset() 호출을 통해 표시된 위치로 되돌려 위치를 재설정 할 수 있다. 다음 예제가 있다:

buffer.mark();

//call buffer.get() a couple of times, e.g. during parsing.

buffer.reset();  //set position back to mark.    

equals() and compareTo()


equals()와 compareTo()를 사용하여 두 버퍼를 비교할 수 있다.

equals()


두 버퍼가 같다면:

  1. 이들은 같은 종류이다(byte, char, int 등)
  2. 이들은 버퍼에 남아있는 bytes, chars 등의 양이 같다.
  3. 남은 모든 bytes, chars 등등이 모두 같다.

알다시피, equals는 내부의 모든 단일 요소가 아닌 Buffer의 부분만을 비교한다. 사실, 이는 Buffer의 남은 요소들만을 비교할 뿐이다.

compareTo()


compareTo() 메소드는 정렬 작업과 같은 곳에서 사용하기 위한 두 버퍼의 남아있는 요소들(bytes, chars 등등)을 비교한다. 하나의 버퍼는 또 다른 버퍼보다 "더 작다" 고 간주된다:

  1. 첫번째 요소가 다른 버퍼의 대응되는 요소와 같다는 것은 다른 버퍼의 것보다 작다
  2. 모든 요소들이 같지만, 첫번째 버퍼가 두번째 버퍼보다 먼저 소모한다(이는 더 적은 요소를 말한다).


'Programming > Java NIO' 카테고리의 다른 글

Channel to Channel Transfers  (0) 2015.09.09
Scatter / Gather  (0) 2015.09.08
Channel  (0) 2015.09.08
Overview  (0) 2015.09.07
Tutorial  (0) 2015.09.07
Posted by 레미파
,