메뉴 바로가기 검색 및 카테고리 바로가기 본문 바로가기

한빛출판네트워크

IT/모바일

Java 6.0 컬렉션 API에 어떤 변화가 있는가?

한빛미디어

|

2007-03-28

|

by HANBIT

11,145

제공: 한빛 네트워크
저자: Vidyasagar.M
역자: 백기선
원문: What is new in Java 6.0 Collections API?

서론

이 글은 Java 6.0 Mustang에서 Collection API에 새로 추가한 것들에 대해 씁니다. Mustang은 Collection API에 일부 변화가 있었습니다. 그것들 중 하나가 Deque입니다. Deque는 양방향으로 탐색 가능한 경우(Bi-Directional traversal)에 사용됩니다. BlockingDeque, ArrayDeque 등 여러 구현체들이 있습니다. Deque와 그것을 구현한 클래스들 그리고 Java 6.0의 Collection의 변화를 살펴보겠습니다.

Java 6.0 의 새로운 컬렉션 API와 개요

다음에 제시된 목록이 Java 6.0에 추가된 새로운 컬렉션API들 입니다. 맨 앞에는 인터페이스, 그 뒤에는 그것을 구현한 클래스들을 적었습니다.

새로운 인터페이스들
  • Deque
  • BlockingDeque
  • NavigableSet
  • NavigableMap
새로운 클래스들
  • ArrayDeque
  • LinkedBlockingDeque
  • ConcurrentSkipListSet
  • ConcurrentSkipListMap
  • AbstractMap.SimpleEntry
  • AbstractMap.SimpleImmutableEntry
Java 6.0 에서 업데이트 된 클래스들
  • LinkedList
  • TreeSet
  • TreeMap
  • Collections
Deque와 ArrayDeque

Deque는 Double Ended Queue의 축약어로 이 컬렉션은 양쪽 끝에서 서로 새로운 요소들을 추가 또는 삭제할 수 있습니다. Deque는 고정된 길이의 컬렉션의 크기와 길이가 고정되어 있지 않은 컬렉션의 사이즈를 알 수 있습니다.

Deque 구현체들은 스택(후입선출 방식의 자료구조)이나 큐(선입선출 방식의 자료구조)로 사용할 수 있습니다. Deque에 새로운 요소를 추가, 삭제, 참조할 때 각각 두 가지 방법이 있습니다. 만약에 해당 오퍼레이션이 실패 했을 때 예외를 발생시키는 방법이 있고 매번 오퍼레이션을 실행 할 때 마다 특정 상태나 값을 반환하도록 하는 방법이 있습니다.

Operation Special value method Exception throwing method
Insertion at head offerFirst(e) addFirst(e)
Removal at head pollFirst() removeFirst()
Retrieval at Head peekFirst() getFirst()
Insertion at Tail offerLast(e) addLast(e)
Removal at Tail pollLast() removeLast()
Retrieval at Tail peekLast() getLast()


Deque를 구현할 때 null값이 추가되는 것을 막을 필요는 없지만 특정 메소드는 null을 반환하는 것이 컬렉션이 비어있다는 것을 뜻하기 때문에 null을 추가하지 못하게 하는 것을 추천합니다.

ArrayDeque는 Deque를 구현한 클래스입니다. 길이에 제한이 없습니다. 이것을 스택으로 사용할 때는 기존의 Strack클래스 보다 성능이 좋고 큐로 사용할 때는 LinkedList 클래스 보다 성능이 좋습니다. ArrayDeque는 멀티 쓰레드를 고려하지 않았습니다. ArrayDeque를 사용한 예제를 보겠습니다.

예제
import java.util.ArrayDeque;
import java.util.Iterator;

public class DequeExample
{
  public static void main(String as[])
  {
    ArrayDeque adObj = new ArrayDeque(); 

    //여러 종류의 메소드를 이용한 추가
    adObj.add("Oracle"); 
    adObj.addFirst("DB2");
    adObj.offerFirst("MySQL");   //boolean 값을 리턴합니다. - true 또는 false 
    adObj.offerLast("Postgres");   //boolean 값을 리턴합니다. - true 또는 false 
    
    //가져오기 
    System.out.println("Retrieving First Element :" + adObj.peekFirst());
    System.out.println("Retrieving Last Element  :"+ adObj.peekLast());

    //제거하기.
    System.out.println("Removing First  Element  :"+ adObj.pollFirst());
    System.out.println("Removing Last  Element   :"+ adObj.pollLast());

    //역순으로 탐색하기
    System.out.println("Remaining Elements :");
    Iterator it = adObj.descendingIterator();
    while(it.hasNext())
    {
      System.out.println(it.next());
    }    
  }
}
출력 결과:
Retrieving First Element :MySQL
Retrieving Last Element  :Postgres
Removing First  Element  :MySQL
Removing Last  Element   :Postgres
Remaining Elements :
Oracle
DB2
BlockingDeque와 LinkedBlockingDeque

BlockingDeque는 Deque와 비슷하고 추가로 여러 기능을 제공합니다. 만약 BlockingDeque가 꽉 찬 상태에서 새로운 요소를 추가하려고 한다면 요소가 들어갈 수 있는 공간이 생길 때까지 기다릴 수 있습니다. 요소가 추가 되길 기다리는 시간을 정해 줄 수 있습니다.

BlockingDeque를 네 가지 방식으로 사용할 수 있습니다. 네 종류의 메소들이 있기 때문입니다.
  • 예외를 발생시키는 메소드
  • 특정 값을 반환하는 메소드
  • 블러킹 하는 메소드(가용한 공간이 생길 때까지 무한대로 기다립니다.)
  • 타임아웃 하는 메소드(가용한 공간이 생길 때까지 정해진 시간 만큼만 기다립니다.)
Operation Special Value Throws Exception Blocks Times out
Insertion at head addFirst(e) offerFirst(e) putFirst(e) offerFirst(e,time,unit)
Removal from head removefirst() pollFirst() takeFirst() takeFirst(time,unit)
Retrieval from head getFirst() peekfirst() NA NA
Insertion at tail addLast(e) offerLast(e) putLast(e) offerLast(e,time,unit)
Removal from tail removeLast() pollLast() takeLast() takeLast(time,unit)
Retrieval from tail getLast() peekLast() NA NA


LinkedBlockingDeque는 BlockingDeque인터페이스를 구현한 클래스로 원하는 대로 최대 크기를 정할 수 있습니다. 만약에 최대 크기를 설정하지 않으면 Integer.MAX_VALUE가 최대 크기로 설정 됩니다.
import java.util.concurrent.*;
class BlockingDequeExample implements Runnable
{
  LinkedBlockingDeque lbd = new LinkedBlockingDeque(1);
  volatile boolean b = true;
  public void run()
  {
    try
    {
      /* 첫 번째 쓰레드가 이 블록으로 들어가면 b 인스턴스 변수를 false로 
      바꾸로 다음 쓰레드가 블록으로 들어가지 못하게 막습니다. */
       if(b)
      {
        b = false;
        Thread.sleep(5000);//쓰레드를 5초간 쉬도록 합니다
        System.out.println("Removing "+lbd.peek());
        lbd.poll();//컬렉션에서 요소 하나를 삭제합니다.
      }
      else
      {
        System.out.println("Waiting ");  
        /*이 메소드는 첫 번째 쓰레드가 요소를 삭제할 때 까지 기다립니다.*/
        lbd.put("B"); 
        System.out.println("Inserted "+lbd.peek());  
      }
    }
    catch (Exception e)
    {
      e.printStackTrace();
    }
  }
  public static void main(String[] args) throws Exception
  {
    BlockingDequeExample bdeObj = new BlockingDequeExample();
    bdeObj.lbd.offer("A");
    System.out.println("Inserted "+bdeObj.lbd.peek());  
    Thread tMainObj = new Thread(bdeObj);
    tMainObj.start();
    Thread tSubObj = new Thread(bdeObj);
    tSubObj.start();    
  }
}
출력 결과:
Inserted A
Waiting
Removing A
Inserted B
NavigableSet와 ConcurrentSkipListSet

정렬되어 있는 콜렉션 [5,10,15,20]에서 다음의 요구 사항들이 있다고 생각해 봅시다.
  • 15 보다 바로 한 단계 작은 요소 하나를 가져오거나 한 단계 큰 요소를 가져오기.
  • 10보다 작은 요소들을 가져오거나 큰 요소들을 모두 가져오기..
기존에 있는 메소드를 가지고 이 요구 사항을 처리하기 에는 다소 부담이 생깁니다. 하지만 NavigableSet의 메소드들을 사용하면 단순한 메소드 호출만으로도 할 수 있습니다. NavigableSet의 메소드들은 컬렉션에 있는 요소들에 가장 비슷하게 매칭되는 요소들을 반환합니다. ConcurrentSkipListSet은 NavigableSet을 구현한 클래스 중 하나 입니다.

예제
import java.util.concurrent.*;
import java.util.*;
class SkipListSetTest 
{
  public static void main(String[] args) 
  {
    ConcurrentSkipListSet csls = new ConcurrentSkipListSet();
    csls.add(15);
    csls.add(20);
    csls.add(5);
    csls.add(10);
    System.out.println("Elements in the collections are");
    for(Integer i: csls)
    {
      System.out.println(i);
    }
    /* 주어진 값보다 바로 한 단계 작은 값 또는 같은 값을 가져옵니다. */
    System.out.println("Floor    "+csls.floor(12));
    /* 주어진 값보다 바로 한 단계 큰 값 또는 값은 값을 가져옵니다. */
    System.out.println("Ceiling  "+csls.ceiling(12));
    /* 주어진 값보다 바로 한 단계 작은 값을 가져 옵니다. */
    System.out.println("Lower    "+csls.lower(10));
    /* 주어진 값보다 바로 한 단계 큰 값을 가져 옵니다. */
    System.out.println("heigher  "+csls.higher(10));
    System.out.println("Head Elements ");
    Set cslsHeadView =  csls.headSet(10);
    //headSet은 주어진 값 보다 큰 값들을 가지게 됩니다.
    for(Integer i: cslsHeadView)
    {
      System.out.println(i);
    }
    Set cslsTailView =  csls.tailSet(10);
    //tailSet은 주어진 값과 같거나 작은 값은 값들을 가지게 됩니다.
    System.out.println("Tail Elements");
    for(Integer i: cslsTailView)
    {
      System.out.println(i);
    }
  }
}
출력 결과:
Elements in the collections are
5
10
15
20
Floor    10
Ceiling  15
Lower    5
heigher  15
Head Elements
5
Tail Elements
10
15
20
NavigableMap과 ConcurrentSkipListMap

NavigablMap은 NavigableSet과 비슷합니다. NavigableSet에있는 메소드들은 주로 값을 반환하는데 반해서 NavigableMap에있는 메소드들은 키와 값의 쌍을 반환합니다. ConcurrentSkipListMap은 NavigableMap을 구현한 클래스 입니다.
import java.util.*;
import java.util.concurrent.*;

class NavigableMapExample 
{
  public static void main(String[] args) 
  {
    NavigableMap nm = new ConcurrentSkipListMap();
    nm.put(1,"One");
    nm.put(2,"Two");
    nm.put(3,"Three");
    nm.put(4,"Four");
    nm.put(5,"Five");
    /* 주어진 키보다 바로 한 단계 작은 키와 값의 쌍을 가져옵니다.*/
    Map.Entry ae = nm.lowerEntry(5);
    /* Map.Entry는 Map인터페이스 내부에 있는 Static 인터페이스 입니다. 
       키와 값의 쌍을 가지고 있는데 사용합니다. */
    System.out.println("Key" + ae.getKey());
    System.out.println("Value"+  ae.getValue());
    /* 주어진 키와 같거나 큰 키와 값의 쌍들을 가져옵니다. */
    SortedMap mm = nm.tailMap(3);
    Set s = mm.keySet(); 
    System.out.println("Tail elements are");
    for(Integer i:s)
    {
      System.out.println("Key "+ i + "Value "+ mm.get(i));
    }
  }
}
출력 결과:
Key 4
Value Four
Tail elements are
Key 3  Value Three
Key 4  Value Four
Key 5  Value Five
요약:

floorEntry 메소드는 주어진 키와 같거나 작은 것들 중 가장 큰 키와 값의 묶음을 반환 합니다. 해당하는 키가 없다면 null을 반환합니다. lowerEntry 메소드는 주어진 키보다 작은 것들 중에서 가장 큰 키와 값의 묶음을 반환 합니다. 해당하는 키가 없다면 null을 반환합니다. headMap 메소드는 주어진 키보다 작은 모든 요소들을 반환합니다. tailMap 메소드는 주어진 키와 같거나 큰 모든 요소들을 반환합니다.

AbstractMap.SimpleEntry와 AbstractMap.SimpleImmutableEntry

AbstractMap.SimpleEntry와 AbstractMap.SimpleImmutableEntry는 AbstractMap 클래스 내부에 있는 static 클래스 입니다. 이 클래스들의 객체는 Map에 있는 단일 요소에 해당하는 키와 값을 가지는데 사용합니다. 이 두 클래스의 차이는 전자의 것(SimpleEntry)은 값을 바꿀 수 있는 반면 후자의 것은 값을 바꾸려는 시도를 하면 UnsupportedOperationException을 발생한다는 것입니다.

변경된 클래스들

기존의 클래스들이 새로운 인터페이스를 구현하도록 수정됐습니다. LinkedList는 Deque를 구현하고 TreeSet은 NavigaeblSet을 TreeMap은 NavigableMap을 구현하도록 수정됐습니다. Collections 인터페이스에는 newSetFromMap과 asLifoQueue가 추가 되었습니다.

결론

Java6.0 컬렉션을 사용하면 양방향 참조가 더 쉽고 원하는 대로 요소들을 가져오는 것이 가능합니다. 컬렉션에 있는 기능들 중 몇몇은 동시성 역시 고려되었습니다.
역자 백기선님은 AJN(http://agilejava.net)에서 자바 관련 스터디를 하고 있는 착하고 조용하며 점잖은 대학생입니다. 요즘은 특히 Spring과 Hibernate 같은 오픈소스 프레임워크를 공부하고 있습니다. 공부한 내용들은 블로그(http://whiteship.tistory.com)에 간단하게 정리하고 있으며 장래 희망은 행복한 개발자입니다.
TAG :
댓글 입력
자료실