자바의 정석 - 컬렉션 프레임웍(Collections Framework)

자바의 정석(남궁성 저) 학습내용 정리

1. 컬렉션 프레임웍 (Collections Framework)

‘데이터 그룹을 다루고 표현하기 위한 단일화된 구조’ - Java API문서
JDK1.2 이전까지는 Vector, Hashtable, Properties와 같은 컬렉션 클래스들을 각자의 방식으로 처리

1.1. 핵심 인터페이스

  • List
    • 순서 있는 데이터 집합
    • 데이터 중복 허용
    • 예시) 대기자 명단
  • Set
    • 순서 없는 데이터 집합
    • 데이터 중복 불허
    • 예시) 양의 정수집합, 소수의 집합
  • Map
    • 키와 값의 쌍으로 이루어진 데이터 집합
    • 순서 없고, 키 중복 불허, 값 중복 허용
    • 어떤 두 값을 연결한다는 의미에서 Map이라 지어짐
    • 예시) 우편번호, 지역번호

Vector나 Hashtable과 같은 기존의 컬렉션 클래스들은 호환을 위해,
설계를 변경해서 남겨두었지만 사용안하는 것이 좋다.
대신 새로 추가된 ArrayList와 HashMap 사용 권장.

1.2. 인터페이스 상속계층도

List 상속계층도 - List - Vector - Stack - ArrayList - LinkedList

Set 상속계층도 - List - HashSet - SortedSet - TreeSet

Map 상속계층도 - Map - Hashtable - HashMap - LinkedHashMap - SortedMap - TreeMap

1.3. Map.Entry 인터페이스

Map.Entry 인터페이스는 Map 인터페이스의 내부 인터페이스이다.
Map에 저장되는 key-value 쌍을 다루기 위해 내부에 Entry 인터페이스 정의.
내부 클래스와 같이 인터페이스 안에 인터페이스를 정의하는 내부 인터페이스가 가능하다.

public interface Map  {
  ...
  interface Entry {
      Object getKey();
      Object getValue();
      Object setValue(Object value);
      boolean equals(Object o);
      int hashCode();
      ...
  }
}

2. ArrayList

ArrayList는 기존의 Vector를 개선한 것.
ArrayList는 Object 배열을 이용해서 데이터를 순차적으로 저장한다.
배열에 더 이상 저장할 공간이 없으면,
보다 큰 새로운 배열을 생성해서 기존 배열에 저장된 내용을 새로운 배열로 복사하고 저장한다.
선언된 배열의 타입이 Object이기 때문에 모든 종류의 객체를 담을 수 있다.

public class ArrayList extends AbstractList
    implements List, RandomAccess, Cloneable, java.io.Serializable  {
        ...
    transient Object[] elementData;  // Object배열
        ...
}

2.1. list의 공통 요소 삭제하기

for(int i = list2.size()-1; i >= 0; i--)  {
    if(list1.contains(list2.get(i)))
        list2.remove(i);
}

위의 코드에서 변수 i를 증가시키면서 삭제하면, 한 요소가 삭제될 때마다
빈 공간을 채우기 위해 나머지 요소들이 자리이동을 해야해서 올바른 결과를 얻을 수 없다.
그래서 i를 감소시키면서 삭제를 해야 자리이동이 발생해도 영향을 받지 않는다.

2.2. 문자열 데이터를 원하는 길이로 잘라서 ArrayList에 담기

import java.util.*;

class ArrayListEx {
  public static void main(String[] args)  {
  
    final int LIMIT = 10; //자르고자 하는 글자의 개수 지정
    String source = "0123456789abcdefghijklmnopqrstuvwxyz"
    int length = source.length();
    
    List list = new ArrayList(length/LIMIT + 10); //크기를 여유있게 잡음
    
    for(int 1=0; i< length; i+=LIMIT) {
        if(i+LIMIT < length)
            list.add(source.substring(i, i+LIMIT));
        else
            list.add(source.substring(i));
    }
    
    for(int i=0; i < list.size(); i++)  {
        System.out.println(list.get(i));
    }
  }
}

단순히 문자열을 특정 크기로 잘라서 출력할 것이라면
charAt(int i)와 for문을 이용하면 되지만,
ArrayList에 담음으로서 더 다양한 작업을 간단하게 처리할 수 있다.

ArrayList나 Vector 같이 배열을 이용한 자료구조는
데이터를 읽어오고 저장하는 데는 효율이 좋지만,
용량을 변경해야할 때는 새로운 배열을 생성한 후
기존 배열로부터 새 배열로 데이터를 복사하기 때문에 효율이 떨어진다.
그러므로 인스턴스를 생성할 때부터 충분한 크기로 생성해야 한다.

2.3. remove() 메소드

Object[] data = null;  // 객체를 담기위한 객체배열 선언
int capacity = 0;  // 용량
int size = 0;  // 크기

public Object remove(int index) {
    Object oldObj = null;
    
    if(index < 0 || index >=size)
        throw new IndexOutOfBoundsException("범위를 벗어났습니다.");
    oldObj = data[index];
    
    if(index != size-1) {
        System.arraycopy(data, index+1, data, index, size-index-1);
    }
    
    data[size-1] = null;
    size--;
    
    return oldObj;
}

배열의 중간에 위치한 객체를 추가하거나 삭제하는 경우
System.arraycopy()를 호출해서 다른 데이터의 위치를 이동시켜야 하기 때문에 작업시간이 오래걸린다.

3. LinkedList

배열은 데이터를 읽어오는데 걸리는 시간이 빠르다는 장점이 있지만,
크기를 변경할 수 없으므로, 새로운 배열을 생성해서 데이터를 복사해야 한다.
그리고 배열의 실행 속도를 향상시키기 위해서는 충분히 큰 크기로 배열을 생성해야하므로 메모리가 낭비된다.

또한, 배열은 차례대로 데이터를 추가하고 마지막에서부터 데이터를 삭제하는 것은 빠르다. 하지만 배열 중간에 데이터를 추가하려면, 빈자리를 만들기 위해 다른 데이터들을 복사해서 이동해야 한다.

3.1. 삭제

간단하다. 삭제하고자 하는 요소의 이전요소가 삭제하고자 하는 요소의 다음요소를 참조하도록 변경하면 끝. 배열처럼 데이터를 이동하기 위해 복사하는 과정이 없기 때문에 처리속도가 매우 빠르다.

3.2. 이중 연결리스트 (Doubly Linked List)

링크드 리스트는 이동방향이 단방향이기 때문에 이전 요소에 대한 접근은 어렵다.
이중 연결리스트는 연결리스트에 참조변수를 하나 더 추가하여, 이전 요소에 대한 참조가 가능

3.3. ArrayList와 LinkedList 비교

  • 순차적인 추가/삭제시 ArrayList가 더 빠르다.
    • 순차적으로 삭제한다는 것은 마지막 데이터부터 역순으로 삭제하는 것이고, ArrayList는 단지 마지막 요소의 값을 null로 바꾸면 된다.
  • 중간 데이터의 추가/삭제시 LinkList가 더 빠르다.
    • LinkedList는 각 요소간의 연결만 변경해주면 되기 때문
    • 반면 ArrayList는 각 요소들을 재배치하여 추가할 공간을 확보하거나 빈공간을 채워야 한다.

4. Stack과 Queue

Stack은 마지막에 저장한 데이터를 가장 먼저 꺼내게 되는 LIFO(Last In First Out)구조.
Queue는 처음에 저장한 데이터를 가장 먼저 꺼내게 되는 FIFO(First In First Out)구조.

4.1. Stack과 Queue 구현

  • Stack
    • 순차적으로 데이터를 추가하고 삭제하는 스택에는 ArrayList와 같은 배열 기반의 컬렉션 클래스가 적합
  • Queue
    • 큐는 데이터를 꺼낼 때 항상 첫번째 저장된 데이터를 삭제한다.
    • 그러므로 ArrayList와 같은 배열 기반의 컬렉션 클래스를 사용한다면, 데이터를 꺼낼 때마다 빈공간을 채우기 위해 데이터 복사가 발생해서 비효율적.
    • 큐는 ArrayList보다 데이터의 추가/삭제가 쉬운 LinkedList가 더 적합하다.

4.2. Stack 메소드

  • boolean empty() : Stack이 비어있는지 알려준다.
  • Object peek() : Stack의 맨 위에 저장된 객체를 반환. pop()과 달리 Stack에서 객체를 꺼내지는 않음
  • Object pop() : Stack의 맨 위에 저장된 객체를 꺼낸다.
  • Object push(Object item) : Stack에 객체(item)를 저장한다.
  • int search(Object o) : Stack에서 주어진 객체(o)를 찾아서 그 위치를 반환. 못찾으면 -1 반환. (배열과 달리 위치는 0이 아닌 1부터 시작)

4.3. Queue 메소드

  • boolean add(Object o) : 지정된 객체를 Queue에 추가한다. 성공하면 true 반환. 저장공간 부족하면 IlligalStateException 발생
  • boolean offer(Object o) : Queue에 객체를 저장. 성공하면true실패하면false` 반환
  • Object remove() : Queue에서 객체를 꺼내 반환. 비어있으면 NoSuchElementException 발생
  • Object poll() : Queue에서 객체를 꺼내서 반환. 비어있으면 null 반환
  • Object element() : 삭제없이 요소를 읽어온다. peek()과 달리 Queue가 비었을 때 NoSuchElementException 발생
  • Object peek() : 삭제없이 요소를 읽어 온다. Queue가 비어있으면 null 반환

Java에서는 스택을 Stack 클래스로 구현하여 제공하고 있지만
큐는 Queue 인터페이스로 정의해놓고 클래스는 제공하지 않는다.
대신 Queue 인터페이스를 구현한 클래스들 중 하나를 선택해서 사용하면 된다.

4.4. Stack 직접 구현하기

import java.util.*;

class MyStack extends Vector  {
  public Object push(Object item) {
    addElement(item);
    return item;
  }
  
  public Object pop() {
    Object obj = peek();  //  Stack에 저장된 마지막 요소를 읽어온다.
    removeElementAt(size()-1); // 마지막 요소를 삭제한다. 배열의 인덱스가 0부터 시작하므로 1을 빼준다.
    return obj;
  }
  
  public Object peek() {
    int len = size();
    if (len == 0)
      throw new EmptyStackException();
    return elementAt(len -1);
  }
  
  public boolean empty()  {
    return size() == 0;
  }
  
  public int search(Object 0)  {
    int i = lastIndexOf(o);   // 끝에서부터 객체를 찾는다.
    if (i >= 0)  {
      return size() - i;
    }
    return -1;  // 해당 객체를 찾지 못하면 -1 반환
  }
}

4.5. Deque(Double-Ended Queue)

Deque는 양쪽 끝에 추가/삭제 가능
Deque는 스택과 큐를 하나로 합쳐놓은 것과 같으며 스택으로 사용할 수도, 큐로 사용할 수도 있다.

Deque Queue Stack
offerLast() offer() push()
pollLast() pop()
pollFirst() poll()
peekFirst() peek()
peekLast() peek()

5. Iterator

컬렉션 프레임웍에서는 컬렉션에 저장된 요소들을 읽어오는 방법을 표준화하였다.
컬렉션에 저장된 각 요소에 접근하는 Iterator인터페이스를 정의하고,
Collection 인터페이스에는 Iterator(Iterator를 구현한 클래스의 인스턴스)를 반환하는 iterator()를 정의하고 있다.

public interface Iterator {
  boolean hasnext();
  Object next();
  void remove();
}

public interface Collection {
  ...
  public Iterator iterator();
  ...
}
  • 컬렉션 클래스에 대해 iterator()를 호출하여 Iterator를 얻은 다음 반복문(주로 while문)을 사용해서 컬렉션 클래스의 요소들을 읽어 온다.

    • boolean hasNext() - 읽어올 요소가 남아있는지 확인. 있으면 true 없으면 false
    • Object next() - 다음 요소를 읽어 온다. next()를 호출하기 전에 hasNext()를 호출해서 읽어 올 요소가 있는지 확인하는 것이 안전.
    • void remove() - next()로 읽어 온 요소를 삭제한다. next()를 호출한 다음에 remove()를 호출해야 한다.
  • Map 인터페이스를 구현한 컬렉션 클래스는 Key와 Value를 쌍으로 저장하기 때문에 iterator()를 직접 호출할 수 없다. 대신 keySet()이나 entrySet()을 통해 Key와 Value를 각각 따로 Set의 형태로 얻어 온 후에 다시 iterator()를 호출해야 Iterator를 얻을 수 있다.

    Map map = new HashMap();
    ...
    Iterator it = map.keySet().iterator();
  • List클래스들은 저장순서를 유지하기 때문에 Iterator를 이용해서 읽어 온 결과 역시 저장 순서와 동일하지만 Set클래스들은 각 요소간의 순서가 유지되지 않기 때문에 Iterator를 이용해서 저장된 요소들을 읽어 와도 처음에 저장된 순서와 같지 않다.

5.1. ListIterator

ListIterator는 Iterator를 상속받아서 기능을 추가한 것으로, 컬렉션의 요소에 접근할 때 Iterator는 단방향으로만 이동할 수 있지만, ListIterator는 양방향으로의 이동이 가능. 단, List인터페이스를 구현한 컬렉션에서만 사용 가능

  • void add(Object o) - 컬렉션에 새로운 객체(o)를 추가
  • hasPrevious() - 읽어 올 이전 요소가 남아있는지 확인. 있으면 true, 없으면 false
  • Object previous() - 이전 요소를 읽어 온다. previous()를 호출하기 전에 hasPrevious()를 호출해서 읽어 올 요소가 있는지 확인하는 것이 안전.
  • int nextIndex() - 다음 요소의 index를 반환
  • int previousIndex() - 이전 요소의 index를 반환
  • void set(Object o) - next() 또는 previous()로 읽어 온 요소를 지정된 객체(o)로 변경. 반드시 next()나 previous()를 먼저 호출한 다음에 이 메서드를 호출해야한다.

6. Arrays

Arrays 클래스에는 배열을 다루는데 유용한 메소드가 정의되어 있다.
Arrays에 정의된 메소드는 모두 static 메소드다.

6.1. 배열 복사 - copyOf(), copyOfRange()

copyOf()는 배열 전체 복사, copyOfRange()는 배열의 일부를 복사해서 새로운 배열을 만들어 반환.
copyOfRange()에 지정된 범위의 끝은 포함되지 않는다.

int[] arr = {0,1,2,3,4,};
int[] arr2 = Arrays.copyOf(arr, 3); // arr2 = [0,1,2]
int[] arr3 = Arrays.copyOfRange(arr, 2, 4); // arr3 = [2,3]

6.2. 배열 채우기 - fill(), setAll()

fill()은 배열의 모든 요소를 지정된 값으로 채운다.
setAll()은 배열을 채우는데 사용할 함수형 인터페이스를 매개변수로 받는다.

int[] arr = new int[5];
Arrays.fill(arr, 9);  // arr = [9,9,9,9,9]
Arrays.setAll(arr, () -> (int)(Math.random()*5)+1); // arr = [1,5,2,1,1]

6.3. 배열 정렬, 검색 - sort(), binarySearch()

sort()는 배열 정렬, binarySearch()는 배열에 저장된 요소를 검색
binarySearch()는 배열에서 지정된 값이 저장된 위치(index)를 찾아서 반환. 반드시 배열이 정렬된 상태여야 올바른 결과를 얻는다.

int[] arr = { 3, 2, 0, 1, 4,};
Arrays.sort(arr); // arr = [0,1,2,3,4]
int idx = Arrays.binarySearch(arr, 2); // idx = 2

6.4. 문자열의 비교, 출력 - equals(), toString()

equals(), toString()은 일차원 배열에만 사용 가능.
다차원 배열에는 deepEquals(), deepToString() 사용.
deepToString()은 배열의 모든 요소를 재귀적으로 접근해서 문자열을 구성하므로 3차원 이상의 배열에도 동작

  • 다차원 배열은 ‘배열의 배열’의 형태로 구성하기 때문에 equals()로 비교하면, 문자열을 비교하는 것이 아니라 ‘배열에 저장된 배열의 주소’를 비교하게 된다.

6.5. 배열을 List로 반환 - asList(Object…a)

asList()는 배열을 List에 담아서 반환.

  • 매개변수의 타입이 가변인수라서 배열 생성 없이 저장할 요소들만 나열하는 것도 가능
  • asList()가 반환한 List의 크기를 변경할 수 없다.
  • 크기 변경가능한 List는 List list = new ArrayList(Arrays.asList(1,2,3,4,5));

7. Comparator와 Comparable

Arrays.sort()를 호출하면 알아서 배열을 정렬하는 것처럼 보이지만,
사실은 Chractor클래스의 Comparable의 구현에 의해 정렬되었던 것.
Comparble을 구현한 클래스는 정렬이 가능하다는 것을 의미.

public interface Comparator {
  int compare(Object o1, Object o2);
  boolean equals(Object obj);
}

public interface Comparable {
  public int compareTo(Object o);
}

Comparable은 java.lang 패키지에 있고, Comparator는 java.util 패키지에 있다.

  • Comparable : 기본 정렬기준을 구현하는데 사용.
  • Comparator : 기본 정렬기준 외에 다른 기준으로 정렬하고자할 때 사용

8. HashSet

HashSet은 Set인터페이스를 구현한 가장 대표적인 컬렉션.
HashSet은 저장순서를 유지하지 않으므로 저장순서를 유지하고자 한다면 LinkedHashSet 사용.

  • 로또번호 예제
import java.util.*;

class HashSetLotto  {
    public static void main(String[] args)  {
        
        Set set = new HashSet();
        
        for (int i = 0; set.size() < 6; i++)  {
            int num = (int)(Math.random()*45) + 1;
            set.add(new Integer(num));
        }
        
        List list = new LinkedList(set);
        Collections.sort(list);
        System.out.println(list);
    }
}

번호를 크기 순으로 정렬하기 위해 Collections클래스의 sort(List list)를 사용했다.
이 메서드는 인자로 List인터페이스 타입을 필요로 하기 때문에
LinkedList클래스의 생성자 LinkedList(Collection c)를 이용해서 HashSet에 저장된 객체들을 LinkedList에 담아서 처리했다.

9. TreeSet

TreeSet은 이진검색트리(binary search tree) 형태로 데이터를 저장하는 컬렉션 클래스다.
TreeSet은 이진검색트리의 성능을 향상시킨 Red-Black tree로 구현되어 있다.
중복된 데이터의 저장을 허용하지 않으며, 정렬된 위치에 저장하므로 저장순서를 유지하지도 않는다.
TreeSet은 정렬된 상태를 유지하기 때문에 단일 값 검색과 범위검색, 예를 들면 3과 7사이의 범위에 있는 값을 검색하는 것이 매우 빠르다.

  • Binary Search Tree

    • 모든 노드는 최대 두 개의 자식노드를 가질 수 있다.
    • 왼쪽 자식노드의 값은 부모노드의 값보다 작고, 오른쪽 자식노드의 값은 부모노드의 값보다 커야한다.
    • 노드의 추가 삭제에 시간이 걸린다.(순차적으로 저장하지 않으므로)
    • 검색(범위검색)과 정렬에 유리하다.
    • 중복된 값을 저장하지 못한다.
  • TreeSet으로 만든 로또번호 생성 프로그램 (HashSet으로 만든 것과 달리 정렬하는 코드가 필요없다. TreeSet은 저장할 때 이미 정렬하기 때문에.)

  import java.util.*;
  
  class TreeSetLotto  {
    public static void main(String[]args) {
      Set set = new TreeSet();
      
      for (int i =0; set.size() < 6; i++) {
        int num = (int)(Math.random()*45) + 1;
        set.add(num); // set.add(new Integer(num));
      }
      
      System.out.println(set);
    }
  }
  • headSet()과 tailSet()을 사용하면, TreeSet에 저장된 객체 중 지정된 기준 값보다 큰 값의 객체들과 작은 값의 객체들을 얻을 수 있다.
  import java.util.*;
  
  class TreeSetEx2  {
    public static void main(String[] args)  {
      TreeSet set = new TreeSet();
      int[] score = {80, 95, 50, 35, 45, 65, 10, 100};

      for (int i=0; i < score.length; i++)
        set.add(new Integer(score[i]));

      System.out.println("50보다 작은 값 :" + set.headSet(new Integer(50)));
      System.out.println("50보다 큰 값 :" + set.tailSet(new Integer(50)));
    }
  }

10. HashMap

Hashtable과 HashMap의 관계는 Vector와 ArrayList의 관계와 같아서 Hashtable보다는 새로운 버전인 HashMap을 사용할 것을 권한다.
HashMap은 Map을 구현했으므로 키와 값을 묶어서 하나의 데이터로 저장한다.
그리고 hashing을 사용하기 때문에 많은 양의 데이터를 검색하는데 뛰어난 성능을 보인다.

public class HashMap extends AbstractMap 
                    implements Map, Cloneable, Serializable  {
  
  transient Entry[] table;
    ...
  
  static class Entry implements Map.Entry {
    final Object key;
    Object value;
      ...
  }
}

HashMap은 Entry라는 내부 클래스를 정의하고, 다시 Entry타입의 배열을 선언한다.
키와 값은 별개의 값이 아니라 서로 관련된 값이기 때문에
각각의 배열로 선언하기 보다는 하나의 클래스로 정의해서 하나의 배열로 다루는 것이 데이터의 무결성(integrity)적인 측면에서 더 바람직하기 때문.

  • HashMap은 키와 값을 각각 Object 타입으로 저장한다.
  • (Object, Object)의 형태로 저장하기 때문에 어떠한 객체도 저장할 수 있지만 key는 주로 String을 대문자 또는 소문자로 통일해서 사용한다.

11. 해싱과 해시함수

해싱이란 해시함수(hash function)를 이용해서 데이터를 해시테이블(hash table)에 저장하고 검색하는 기법
해시함수는 데이터가 저장된 곳을 알려주기 때문에 다량의 데이터 중에서도 원하는 데이터를 빠르게 찾을 수 있다.

  • 해싱을 구현한 함수 - HashSet, HashMap, Hashtable
  • Hashtable은 Collection Framework가 도입되면서 HashMap으로 대체되었으나 이전소스와의 호환성 문제로 남겨두고 있다.

11.1. hashCode()

  • HashMap과 같이 해싱을 구현한 컬렉션 클래스에서는 Object클래스에 정의된 hashCode()를 해시함수로 사용한다.
  • Object클래스에 정의된 hashCode()는 객체의 주소를 이용하는 알고리즘으로 해시코드를 만들기 때문에 모든 객체에 대해 hashCode()를 호출한 결과가 서로 유일한 훌륭한 방법이다.
  • String클래스의 경우 Object로 부터 상속받은 hashCode()를 오버라이딩해서 문자열의 내용으로 해시코드를 만들어낸다. 그래서 서로 다른 String인스턴스일지라도 같은 내용의 문자열을 가졌다면 hashCode()를 호출하면 같은 해시코드를 얻는다.

12. TreeMap

TreeMap은 이름에서 알 수 있듯이 이진검색트리의 형태로 키와 값의 쌍으로 이루어진 데이터를 저장한다.
그래서 검색과 정렬에 적합한 클래스이다.
검색의 경우 대부분의 경우에서 HashMap이 TreeMap보다 더 뛰어나다.
다만 범위검색이나 정렬이 필요한 경우 TreeMap이 낫다.

13. Properties

Properties는 HashMap의 구버전인 Hashtable을 상속받아 구현한 것이다.
Hashtable은 키와 값을 (Object, Object)의 형태로 저장하는데 비해
Properties는 (String, String)의 형태로 저장하는 보다 단순화된 컬렉션클래스이다.

  • 주로 애플리케이션의 환경설정과 관련된 속성(property)을 저장하는데 사용된다.
  • 데이터를 파일로부터 읽고 쓰는 편리한 기능을 제공한다.
  • Properties는 컬렉션프레임웍 이전의 구버전이므로 Iterator가 아닌 Enumeration을 사용한다.
  • list메소드로 Properties에 저장된 모든 데이터를 화면 또는 파일에 편하게 출력할 수 있다.
  void list(PrintStream out)
  void list(PrintWriter out)

14. Collections

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

14.1. 컬렉션의 동기화

  • 멀티 쓰레드(multi-thread) 프로그래밍에서는 하나의 객체를 여러 쓰레드가 동시에 접근할 수 있기 때문에 데이터의 일관성(consistency)을 유지하기 위해서는 공유되는 객체에 동기화(synchronization)가 필요하다.
  • 새로 추가된 ArrayList와 HashMap과 같은 컬렉션은 동기화를 자체적으로 처리하지 않는다.
  • 필요한 경우에만 java.util.Collections 클래스의 동기화 메소드를 이용해서 동기화처리가 가능하도록 변경하였다.

15. 컬렉션 클래스 요약

  • ArrayList : 배열기반. 데이터의 추가와 삭제에 불리. 순차적인 추가삭제는 제일 빠름. 임의의 요소에 대한 접근성이 뛰어남.
  • LinkedList : 연결기반. 데이터의 추가와 삭제에 유리. 임의의 요소에 대한 접근성이 좋지 않다.
  • HashMap : 배열과 연결이 결합된 상태. 추가,삭제,검색,접근성이 모두 뛰어남. 검색에는 최고의 성능.
  • TreeMap : 연결기반. 정렬과 검색(특히 범위검색)에 적합. 검색성능은 HashMap보다 떨어짐.
  • Stack : Vector를 상속받아 구현
  • Queue : LinkedList가 Queue인터페이스를 구현
  • Properties : Hashtable을 상속받아 구현
  • HashSet : HashMap을 이용해서 구현
  • TreeSet : TreeMap을 이용해서 구현
  • LinkedHashMap : HashMap과 HashSet에 저장순서유지기능 추가
  • LinkedHashSet : HashMap과 HashSet에 저장순서유지기능 추가