Akashic Records

자바 nio - 5 본문

오래된글/Java

자바 nio - 5

Andrew's Akashic Records 2018. 4. 9. 11:24
728x90

5. Buffer의 하위 클래스

버퍼류 클래스에는 boolean형을 제외한 나머지 기본형 데이터형에 맞는 Buffer 클래스들이 있다. 이들 클래스들은 각각의 데이터형에 맞는 여러 기능의 메서드를 가지고 있다. 또한 이들 하위 클래스들의 특징은 모두들 abstract 클래스인데 그 이유는 Heap Buffer와 Direct Buffer의 구분때문이다. 이들을 객체화 할때 allocate()메서드를 이용하는데 이때는 Heap Buffer를 사용한다. Direct Buffer를 사용하기 위해서는 ByteBuffer 클래스의 allocateDirect()메서드를 이용하는데 이는 ByteBuffer만 가능하다. 하지만 다른 유형의 버퍼로 변환하는 것은 가능하다. 따라서 버퍼류 클래스 객체 생성을 하는 allocate()메서드는 각각 클래스에 맞게 정의되어 있어서 그형의 클래스 객체를 리턴한다.

각 버퍼류 클래스마다 공통된 메서드가 거의 주류를 이루므로 한꺼번에 설명하겠다. 기능은 같으나 각 인자형이나 리턴형은 각 클래스에 따라 다르다라는 것을 알고 하나씩 살펴보자.


1. Buffer의 하위 클래스들의 주요 메서드

- allocate(int capacity)

: 각각의 버퍼 클래스형 객체 리턴(Heap Buffer)

- wrap(기본형 데이터 배열)

- wrap (기본형 데이터 배열, int offset, int length)

: 해당 기본형 데이터 유형 배열을 인자로 주면 이 배열이 해당 버퍼에 put되어서 클래스형 객체 리턴(Heap Buffer), 즉 버퍼생성과 동시에 데이터 저장이다.

- array()

: 현재 버퍼가 데이터를 저장하는데 쓰고 있는 기본형 데이터형의 배열을 리턴한다. 단 이 메서드는 hasArray() 메소드가 true를 리턴하는 경우에만 사용한다.hasArray() 메소드가 true를 리턴하려면 Direct Buffer가 아니며 데이터 관리를 배열로 하고 있는 경우여야 한다.

- abstract boolean isDirect ()

: 현재 버퍼가 Direct Buffer인지 아닌지를 리턴. Direct Buffer이면 true를 리턴한다.

- asReadOnlyBuffer()

: read 만 가능한 버퍼를 리턴.

- compact()

: 현재 position과 limit 사이에 남아있는 데이터들을 모두 버퍼 맨 앞쪽으로 이동시킨다. 그리고 position은 이들 데이터의 맨 마지막 칸으로 동하고 limit는 capacity와 같은 값을 가진다. 이메서드는 다른 메서드와는 달리 위치값뿐만아니라 데이터들의 이동이 있다는 점이 다르다.

예를 들어 다음 그림과 같이 현재 position이 4이고 limit가 7이라고 하자.

1

2

3

4

5

6

7

8

9

10


0

1

2

3

4

5

6

7

8

9





(pos=4)


(limit=7)



(cap=10)


이 상태에서 compact()메서드를 호출하면 position과 limit 사이의 데이터들을 맨 앞으로 이동시키고 위치는 그 다음 칸으로 이동하고 limit는 capacity와 같은 값을 가진다.

5

6

7

4

5

6

7

8

9

10


0

1

2

3

4

5

6

7

8

9




(pos=3)





(limit=cap=10)



- slice()

: 현재 position과 limit 의 범위를 별도의 버퍼로 만들어서 리턴해 준다. 즉 잘라내기라 생각하면 된다. 이것은 데이터는 공유하지만 position과 limit , mark값을 별도로 갖는 것으로 어느 한쪽에서 데이터를 수정하면 다른 한쪽도 수정한 데이터를 볼 수 있다. 이렇게 만들어진 새로운 버퍼는 position는 0으로, limit는 capacity와 같은 값을 가진다. mark는 재설정해 주어야 한다. 또 버퍼가 Heap Buffer라면 새로 생긴 버퍼도 Heap Buffer이며, 읽기전용이면 새 버퍼도 읽기전용이다.

예를 들어 다음 그림과 같은 버퍼를 slice()를 호출해서 새로 버퍼를 만든다면.. 그림과 같다.

1

2

3

4

5

6

7

8

9

10


0

1

2

3

4

5

6

7

8

9





(pos=4)


(limit=7)



(cap=10)


5

6

7


0

1

2


<--새로 생긴 버퍼

(pos=0)



(limit=cap=10)



- duplicate()

: 데이터를 공유하는 또 하나의 버퍼를 생성해 주는데 이는 데이터 값만 복사되는 slice()와는 달리 position과 limit , mark 같은 모든 값이 그대로 복사된다. 즉 통째로 복사가 된다. 다만 복사된 이후에 position과 limit , mark값을 별도로 가진다. 그래서 내용만 공유하고 position과 limit , mark값는 따로 조작이 가능하다.

예를 들어 다음 그림과 같은 버퍼를 duplicate()를 호출해서 새로 버퍼를 만든다면.. 그림과 같다.

1

2

3

4

5

6

7

8

9

10


0

1

2

3

4

5

6

7

8

9





(pos=4)


(limit=7)



(cap=10)


데이터를 공유하는 또 다른 버퍼 생성

1

2

3

4

5

6

7

8

9

10


0

1

2

3

4

5

6

7

8

9





(pos=4)


(limit=7)



(cap=10)


2. 예제


import java.nio.*;

class SubBuffer {

public static void main(String[] args) {


System.out.println("-----------wrap()로 배열을 통째로 버퍼에 넣기 ");

int i[]={10,20,30,40,50,60,70,80,90,100};

// wrap()로 버퍼 생성과 동시에 데이터 put

IntBuffer buf=IntBuffer.wrap(i);

// 제대로 들어갔는지 데이터를 get해서 확인

while(buf.hasRemaining()){

System.out.print(buf.get()+",");

}

// position/limit/capacity확인

System.out.println("\n넣은후 : "+buf);

System.out.println("\n-----------array()로 반대로 버퍼를 배열로------");

// 버퍼의 데이터를 배열로 리턴, 이때 hasArray()가 true를 리턴해야 된다.

if(buf.hasArray()){

int a[]=buf.array();

// 배열 출력

for(int x=0;x<a.length;x++)

System.out.print(a[x]+",");

}

System.out.println("\n넣은후 : "+buf);

System.out.println("\n-----------compact()로 3부터 10까지 테이터를 맨앞으로------ ");

buf.position(3);

buf.compact();

System.out.println(buf);

for(int s=0;s<10;s++){

System.out.print(buf.get(s)+",");//출력

}

System.out.println("\n\n-----------duplicate()로 복사하기------- ");

IntBuffer buf2=buf.duplicate();

System.out.println("복사\n"+buf2);

buf.position(3);

System.out.println("원본 pos를 3으로 변경 :\n"+buf);

System.out.println("복사본은 ?:\n"+buf);

System.out.println("복사본 내용--");

for(int s=0;s<10;s++){

System.out.print(buf2.get(s)+",");//출력

}


System.out.println("\n\n-----------slice()로 자르기 ------");

IntBuffer buf3=buf.slice();

System.out.println("자르기\n"+buf3);

buf.position(3);

System.out.println("\n원본 pos를 3으로 변경 :\n"+buf);

System.out.println("자른 pos은 ?:\n"+buf3);

System.out.println("자른본 내용--");

while(buf3.hasRemaining()){

System.out.print(buf3.get()+",");//출력

}

System.out.println("\n\n-----------원본 데이터를 바꾼다. ------");

buf.clear();

for(int s=0;s<5;s++){

buf.put(s);//0에서 4까지 버퍼에 저장

}

System.out.println("\n바뀐 원본 : ");

for(int s=0;s<10;s++){

System.out.print(buf.get(s)+",");//출력

}

buf2.clear();

System.out.println("\n복사본은? : ");

for(int s=0;s<10;s++){

System.out.print(buf2.get(s)+",");//출력

}


buf3.clear();

System.out.println("\n자른본은? : ");

while(buf3.hasRemaining()){

System.out.print(buf3.get()+",");//출력

}

}

}


<< 실행 결과 >>


C\>java SubBuffer

-----------wrap()로 배열을 통째로 버퍼에 넣기

10,20,30,40,50,60,70,80,90,100,

넣은후 : java.nio.HeapIntBuffer[pos=10 lim=10 cap=10]

-----------array()로 반대로 버퍼를 배열로------

10,20,30,40,50,60,70,80,90,100,

넣은후 : java.nio.HeapIntBuffer[pos=10 lim=10 cap=10]

-----------compact()로 3부터 10까지 테이터를 맨앞으로------

java.nio.HeapIntBuffer[pos=7 lim=10 cap=10]

40,50,60,70,80,90,100,80,90,100,

-----------duplicate()로 복사하기-------

복사

java.nio.HeapIntBuffer[pos=7 lim=10 cap=10]

원본 pos를 3으로 변경 :

java.nio.HeapIntBuffer[pos=3 lim=10 cap=10]

복사본은 ?:

java.nio.HeapIntBuffer[pos=3 lim=10 cap=10]

복사본 내용--

40,50,60,70,80,90,100,80,90,100,

-----------slice()로 자르기 ------

자르기

java.nio.HeapIntBuffer[pos=0 lim=7 cap=7]

원본 pos를 3으로 변경 :

java.nio.HeapIntBuffer[pos=3 lim=10 cap=10]

자른 pos은 ?:

java.nio.HeapIntBuffer[pos=0 lim=7 cap=7]

자른본 내용--

70,80,90,100,80,90,100,

-----------원본 데이터를 바꾼다. ------

바뀐 원본 :

0,1,2,3,4,90,100,80,90,100,

복사본은? :

0,1,2,3,4,90,100,80,90,100,

자른본은? :

3,4,90,100,80,90,100,


소스가 좀 길다. 그 이유는 확인을 하기위한 출력문이 많아서 그런것 뿐 이런것들을 생략하면 반으로 줄어든다. 자 하나씩 그림과 함께 살펴보자.

1. 버퍼를 wrap() 로 생성과 동시에 배열을 데이터로 put()

10

20

30

40

50

60

70

80

90

100


0

1

2

3

4

5

6

7

8

9

(pos=0)








(limit=cap=10)



2. array()메서드로 버퍼에 저장된 데이터를 배열로 리턴 받는데 반드시 hasArray()가 true를 리턴해야 된다.

3. 우선 position(3)으로 버퍼의 현 위치를 3으로 설정한 다음 campact()메서드를 호출한다. 그러면 위치 3에서 limit가 10이므로 이 사이의 데이터 7개가 버퍼 맨 앞으로 이동하고 위치(pos)는 7로 이동, limit는 capacity와 같은 값을 가진다.

40

50

60

70

80

90

100

80

90

100


0

1

2

3

4

5

6

7

8

9








(pos=7)

(limit=cap=10)



4. duplicate() 메서드를 호출, 버퍼를 복사해서 새로운 버퍼를 buf2에 담는다. 이때 pos/limit/cap 값들이 그대로 복사됨을 알 수 있다.

40

50

60

70

80

90

100

80

90

100


0

1

2

3

4

5

6

7

8

9

<- buf(원본)








(pos=7)

(limit=cap=10)




40

50

60

70

80

90

100

80

90

100


0

1

2

3

4

5

6

7

8

9

<- buf2(복사본)








(pos=7)

(limit=cap=10)




5. 복사를 하고 나서 원본인 buf의 position을 3으로 바꾸어 보니 복사본인 buf2도 position가 3으로 바뀐것을 확인 할 수 있다.

6. 이번에도 버퍼를 하나 더 생성하는데 slice()를 이용해서 새로 생긴 버퍼를 buf3에 담든다.그럼 지금 원본인 buf의 pos가 3이고 limit가 10이므로 이 범위의 데이타로 buf3를 생성하므로 다음과 같은 그림이다.

40

50

60

70

80

90

100

80

90

100


0

1

2

3

4

5

6

7

8

9

<- buf(원본)




(pos=3)





(limit=cap=10)




70

80

90

100

80

90

100


0

1

2

3

4

5

6

<- buf3(자른본)

(pos=0)





(limit=cap=7)




7. 역시 여기서도 원본인 buf의 position을 3으로 바꾸었다. 하지만 복사본과는 달리 자르기를 한 buf3는 전혀 변화가 없음을 알 수 있다.


8. 이번에는 원본의 데이터를 바꾸면 복사본과 자른 버퍼의 데이터도 바뀌는 지를 알아본다. 우선 원본인 buf의 데이터의 위치 0번부터 5번까지 데이터를 0에서 4로 바꾸었다. 그리고 나서 복사본과 자른본의 데이터를 출력하니 역시 데이터가 바뀐것을 알 수있다. 이는 복사본과 자른본은 원본과 데이터를 공유한다는 것을 알 수있다. 하지만 복사본은 원본과 pos나 limit값들도 함께 공유하지만 자른본은 데이터만 공유함을 알 수 있다.

조금 복잡해 보이나 그림을 그려가면서 따져보면 이해할 수 있다. 자 그럼 버퍼류클래스들을 살펴볼텐데 대부분이 같은 기능을 가진다. 그중에서 추가 기능을 가진 ByteBuffer와 CharBuffer에 대해서 알아보자.


728x90

'오래된글 > Java' 카테고리의 다른 글

자바 nio - 7  (0) 2018.04.09
자바 nio - 6  (0) 2018.04.09
자바 nio - 4  (0) 2018.04.09
자바 nio - 3  (0) 2018.04.09
자바 nio - 2  (0) 2018.04.09
Comments