Collections

Arrays가 배열과 관련된 메서드를 제공하는 것 처럼, Collections는 컬렉션과 관련된 메서드를 제공한다. fill()(컬렉션 채우기), copy()(복사), sort()(정렬), binarySearch()(검색) 등의 메서드는 두 클래스에 모두 포함되어 있으며 같은 기능을 한다.

1. 컬렉션의 동기화: synchronizedXXX()

컬렉션 프레임워크의 대부분의 클래스들은 싱글 스레드 환경에서 사용할 수 있도록 설계되었다. 그렇기 때문에 여러 쓰레드가 동시에 컬렉션에 접근한다면 의도하지 않게 요소가 변경될 수 있는 불안전한 상태가 된다.

멀티 쓰레드(multi-thread) 프로그래밍에서는 하나의 객체를 여러 쓰레드가 동시에 접근할 수 있기 때문에 데이터의 일관성(consistency)을 유지하기 위해서는 공유되는 객체에 동기화(synchronization)가 필요하다.

  • 구버전의 클래스들 (VectorHashtable): 자체적으로 동기화 처리가 되어있음.
    • 멀티쓰레드 프로그래밍이 아닌경우 불필요한 기능이 되어 성능을 저하시킨다.
    • 이를 개선하고자 새로 추가된 컬렉션(ArrayListHashMap)은 필요한 경우에만 동기화 처리가 가능하도록 변경되었다.

Collections 클래스에서는 다음과 같은 동기화 메서드를 제공하므로, 동기화가 필요할 때 해당하는 것을 사용하면 된다.

static Colelction synchronizedCollection(Collection c)
static List synchronizedList(List list)
static Set synchronizedSet(Set s)
static Map synchronizedMap(Map m)
static SortedSet synchronizedSortedSet(SortedSet s)
static SortedMap synchronizedSortedMap(SortedMap m)

이들을 사용하는 것은 아래와 같이 사용하면 된다.

List syncList = Collections.synchronizedList(new ArrayList(...));
Collection 동기화된컬렉션 = Collections.synchronizedXXX(동기화 되지 않은 컬렉션);

2. 변경불가(readOnly) 컬렉션: unmodifiableXXX()

final int와 같이 변수를 상수화시켜 변경이 불가능하도록 해야하는 경우가 있다. 컬렉션 역시 저장된 데이터를 보호하기 위해 데이터가 변경할 수 없게, 즉 읽기 전용으로 만들어야 할 때가 있다.

  • 아래의 메서드들을 사용해 변경불가 컬렉션을 생성할 수 있다.
    • 멀티 쓰레드 프로그래밍에서 여러 쓰레드가 하나의 컬렉션을 공유하다보면 데이터가 손상될 수 있는데, 이를 방지할 수 있다.
static Collection unmodifiableCollection(Collection c)
static List unmodifiableList(List list)
static Set unmodifiableSet(Set s)
static Map unmodifiableMap(Map m)
static NavigableSet unmodifiableNavigableSet(NavigableSet s)
static SortedSet unmodifiableSortedSet(SortedSet s)
static NavigableMap unmodifiableNavigableMap(NavigableMap m)
static SortedMap unmodifiableSortedMap(SortedMap m)

3. 싱글톤(Singleton) 컬렉션: singletonXXX()

  • 단 하나의 객체만을 저장(객체 1개만 저장)하는 컬렉션을 만들고 싶을때 사용할 수 있다.
    • 매개변수로 저장할 요소를 지정하면, 해당 요소를 저장하는 컬렉션을 반환한다.
    • 반환한 컬렉션은 변경할 수 없다.
static List singletonList(Object o)
static Set singleton(Object o) // singleSet이 아님에 주의
stiatc Map singletonMap(Object key, Object value)

활용 예시

  • 설정 파일이나 초기화 파라미터 등, 변경되지 않는 단일 데이터를 저장하는 경우
  • 함수나 메소드의 파라미터로 컬렉션을 요구할 때, 단 하나의 요소만 전달하고 싶은 경우
    • 이 때, 불변성을 보장받을 수 있기 때문에, 실수로 데이터를 변경하는 것을 방지할 수 있습니다.
  • 메모리 사용을 최적화하고 싶은 경우
    • 단일 요소를 가진 컬렉션을 여러 번 생성해야 하는 상황에서, 각각의 컬렉션을 별도로 생성하는 대신 싱글톤 컬렉션을 사용하면, 해당 요소에 대한 참조만을 저장하므로 메모리 사용량을 줄일 수 있습니다.

4. 한 종류의 객체만 저장하는 컬렉션: checkedXXX()

  • 컬렉션에 모든 종류의 객체를 저장할 수 있다는 것은 장점인 동시에 단점일 수 있다.
    • 대부분의 경우 한 종류의 객체를 저장하며, 컬렉션에 지정된 종류의 객체만 저장할 수 있도록 제한하고 싶을때 아래의 메서드를 사용한다.
static Collection checkedCollection(Collection c, Class type)
static List checkedList(List list, Class type)
static Set checkedSet(Set s, Class type)
static Map checkedMap(Map m, Class keyType, Class valueType)
static Queue checkedQueue(Queue queue, Class type)
static NavigableSet checkedNavigableSet(NavigableSet s, Class type)
static SortedSet checkedSortedSet(SortedSet s, Class type)
static NavigableMap checkedNavigableMap(NavigableMap m, Class keyType, Class valueType)
static SortedMap checkedSortedMap(SortedMap m, Class keyType, Class valueType)

사용 예시

List list = new ArrayList();
List checedList = checkedList(list, String.class); // String만 저장가능
checkedList.add("abc");
checkedList.add(new Integer(3)); // 에러. ClassCastException 발생

지네릭스를 사용하면 되지 않나요?

컬렉션에 저장할 요소의 타입을 제한하는 것은 지네릭스(generics)로 간단히 처리할 수 있다. 그럼에도 불구하고 해당 메서드들을 제공하는 이유는 호환성 때문이다. 지네릭스는 JDK 1.5부터 도입된 기능으로 JDK 1.5 이전에 작성된 코드를 사용할 때 이 메서드들이 필요할 수 있다.

5. 병렬 처리를 위한 컬렉션: ConcurrentXXX()

동기화된(synchronized) 컬렉션은 멀티 스레드 환경에서 하나의 스레드가 요소를 안전하게 처리하도록 도와주지만, 전체 요소를 빠르게 처리하지는 못한다. 하나의 요소를 처리할 때 전체 잠금이 발생하여 다른 스레드는 대기 상태가 된다. 그렇기 때문에 멀티 스레드가 병렬적으로 컬렉션의 요소들을 처리할수 없다.

자바는 멀티 스레드가 컬렉션의 요소를 병렬적으로 처리할 수 있도록 특별한 컬렉션을 제공하고 있다.

  • java.util.concurrent 패키지의 ConcurrentHashMapConcrrentLinkedQueue이다.
    • ConcurrentHashMap은 Map 구현 클래스이고, ConcurrentLinkedQueue는 Quene 구현 클래스이다.

ConcurrentHashMap

ConcurrentHashMap: 스레드가 안전하면서도 멀티 스레드가 요소를 병렬적으로 처리할 수 있다.

  • 부분(segment) 잠금을 사용하기 때문.
    • 부분 잠금: 처리하는 요소가 포함된 부분만 잠금하고 나머지 부분은 다른 스레드가 변경할 수 있도록 하는 것이 부분 잠금이다.
    • cf) 전체 잠금: 컬렉션에 10개의 요소가 저장되어있을 경우, 1개를 처리할 동안 전체 10개의 요소를 다른 스레드가 처리하지 못하도록 하는 것

다음은 ConcurrentHashMap 객체를 생성하는 코드이다. 다른 Map 구현 객체와 마찬가지로 Map 인터페이스의 메서드를 호출하면 된다.

Map<K, V> map = new ConcurrentHashMap<K, V>();

ConcurrentLinkedQueue

ConcurrentLinkedQueue: 락-프리(lock-free) 알고리즘을 구현한 컬렉션.

  • 락-프리(lock-free) 알고리즘: 여러 개의 스레드가 동시에 접근하는 경우, 잠금을 사용하지 않고도 최소한 하나의 스레드가 안전하게 요소를 저장하거나 얻도록 해준다.

다음은 ConcurrentLinkedQueue를 생성하는 코드이다.

Queue<E> queue = new ConcurrentLinkedQueue<E>();

Reference

  • [남궁성] 자바의 정석
  • [신용권, 임경균] 이것이 자바다
profile
Good Luck!

0개의 댓글

Powered by GraphCDN, the GraphQL CDN