컬렉션(Collection)

언어로그/Java 2011. 4. 11. 23:26



자바에서는 효율적인 데이터 처리를 위해 일련의 컨테이너 자료구조 라이브러리를 제공하고 있다.  데이터를 담는다는
의미에서 컨테이너라고 하며,  최상위 추상클래스 Collection의 이름을 따서 컬렉션이라고 부르기도 한다. 
데이터를 다루는 방법과 데이터에 대한 조작 연산(CRUD)의 빈도에 따라 최적화되어 있는 다양한 자료구조들을 제공하고 있다. 
이 컬렉션의 클래스들에 대한 모두 기억할 필요는 없다.  데이터 추가/조회/수정/삭제 중 어떤 연산이 많은 빈도를 가지고 있는지에 따라 어떤 컬렉션 클래스가 적합한지 알고 사용하면 되겠다.  각 컬렉션들에 대한 구체적인 설명은 생략하겠다. 
컬렉션류 클래스들의 전체 상속도는 다음과 같다







컬렉션 클래스들 중 Set 과 HashMap 대한 특성을 알아보자. 



1. Set
기본형 타입 외 사용자 정의타입에 대해 Set을 사용하면 데이터들의 중복이 없으며 element  들의 순서를 유지하지 않는다.
(집합과 같이 데이터들의 순서가 중요하지 않으며,  데이터들이 중복없이 하나만 고유하게 존재할 경우 사용한다)
 Set (인터페이스)  중복요소 저장 안됨.
 추가되는 요소는 객체의 동일성 판별을 위해 equals() 메소드를 정의해야함.
  Collection 과 인터페이스가 동일하며, 요소(element)들의 순서유지가 보장되지 않음.
 HashSet  빠른 검색이 가능한 Set.
 요소는 equals(), hashCode()를 정의해야함. 
 TreeSet  트리형태의 순서를 갖는 Set.
 정렬된 요소를 얻을 수 있으며, Comparable 인터페이스를 구현해야 함.
 LinkedHashSet  HashSet의 빠른 검색속도를 갖으며,  내부적으로 LinkedList의 순서를 유지함.
 추가된 순서대로 요소를 얻을 수 있음.
 hashCode() 메소드 또한 정의해야함. 

Set을 사용자 정의 타입에서(클래스에) 사용하려면, 중복성 회피를 위해 equals() 메소드를 재정의 해주어야한다. 
(Set는 내부적으로 element 들의  equals() 메소드를 호출하여 서로간의 중복 여부를 판단하기 때문이다)
hashCode()는 hashSet,  LinkedHashSet 를 사용할 때만 재정의 해주면 된다.  재정의를 하지 않으면,  Object의  hashCode()를
사용하는데,  equals()를 구현했어도 객체가 중복되어 Set 인터페이스 규약이 깨지게 된다.   (서로 동일한 데이터를 가지고 있어도hashCode()를 구현하지 않으면 다른 버킷으로 해싱되기 때문  데이터가 중복될수 있다. 이것은 Set의 규약을 위반한 것임)
hashCode(), equals() 을 모두 재정의 하는 것이 바람직한 습관이다. 




2. Map
key-value  관계를 저장하여 key를 사용한 value의 검색을 가능하게 함.
 HashMap   해시 테이블에 기초한 구현 클래스.
 객체 추가 / 검색어에 동일한 시간을 보장함.
 LinkedHashMap  HashMap과 동일한 저장방식을 사용함.
 이터레이터를 사용함. key-value 쌍이 추가된 순서, 최근 최소 사용한 빈도에 따른 검색이 가능
 HashMap 에 비해 다소 느리고, 순차검색시 내부적으로 연결리스트를 사용해 더빠른 성능을 발휨. 
 TreeMap  이진 트리에 기초한 맵 구현클래스.
 key-value 쌍은 Comparable, Comparator에 의해 정렬순서가 저장됨.
하위 트리를 반환하는 subMap() 메소드를 보유함. 
 WeakHashMap  더 이상 참조되지 않은 key에 대한 가비지 컬렉션을 허용함.
 ConcurrentHashMap   동기화에서 락을 발생시키지 않는 Thread-Safe한 Map.
 IdentityHashMap   key 비교시 equals()가 아닌 == 사용함. 범용사용은 불가능함. 





예제
import java.util.*;

// 모든 Set 컬렉션은
// 중복 회피를 위해 equals()를 재정의 해야함
class SetType {
        int i;
        public SetType(int n) {
                i = n;
        }
        public boolean equals(Object o) {
                return o instanceof SetType && (i == ((SetType)o).i);
        }
        public String toString() {
                return Integer.toString(i);
        }
}

// HashSet 계열은 equals() 외에 hasCode()를 재정의 해야함
// 그렇지 않을 경우 equals()를 정의했어도, 데이터 중복이 발생
class HashType extends SetType {
        public HashType(int n) {
                super(n);
        }
        public int hashCode() {
                return i;
        }
}

// TreeSet 계열은 equals() 외에 compareTo()를 재정의 해야함
// compareTo()를 재정의 하지않으면 실행시 ClassCastException 발생
class TreeType extends SetType implements Comparable<treetype>
 {
        public TreeType(int n) {
                super(n);
        }
        public int compareTo(TreeType arg) {
                return (i > arg.i ? 1 : (i == arg.i) ? 0 : -1);
        }
}


public class TypesForSets {
        
        static <t> Set<t> fill(Set<t> set, Class<t> type) {
                try {
                        for (int i = 0; i < 10; i++) {
                                // int형 인자를 갖는 생성자를 얻어서, 객체 생성
                                set.add(type.getConstructor(int.class).newInstance(i));
                        }
                } catch(Exception e) {
                        throw new RuntimeException(e);
                }
                return set;
        }
        
        static <t> void test(Set<t> set, Class<t> type) {
                // 객체 중복저장 시도 - 셋의 특성을 알아보기 위함
                fill(set, type);
                fill(set, type);
                fill(set, type);
                System.out.println(set);
        }
        
        public static void main(String[] args) {
                // HashSet 계열은 equals() 와 hashCode()를 재정의 해야함
                test(new HashSet(), HashType.class);
                test(new LinkedHashSet(), HashType.class);
                // TreeSet 계열은 equals() 와 compareTo()를 재정의 해야함
                test(new TreeSet(), TreeType.class);
                
                // 아래메소드들은 각 Set 컬렉션의 특성이 파괴됨
                test(new HashSet(), SetType.class);
                test(new HashSet(), TreeType.class);
                test(new LinkedHashSet(), SetType.class);
                test(new LinkedHashSet(), TreeType.class);
                
                try {
                        test(new TreeSet(), SetType.class);
                } catch (Exception e) {
                        System.out.println(e.getMessage());
                }
                try {
                        test(new TreeSet(), HashType.class);
                } catch (Exception e) {
                        System.out.println(e.getMessage());
                }
        }
}



실행결과