



ArrayList는 컬렉션 프레임웍에서 가장 많이 사용되는 컬렉션 클래스이다.
이는 List인터페이스를 구현하기 때문에 데이터의 저장순서가 유지되고 중복을 허용한다는 특징을 갖는다.
ArrayList는 Object배열을 이용해서 데이터를 저장한다. 배열에 더 이상 저장할 공간이 없으면 보다 큰 새로운 배열을 생성해서 기존의 배열에 저장된 내용을 새로운 배열로 복사한 다음에 저장한다.
import java.util.*;
class ArrayListEx1{
public static void main(String[] args){
ArrayList list1 = new ArrayList(10);
list.add(new Integer(5));
list.add(new Integer(4));
list.add(new Integer(2));
list.add(new Integer(0));
list.add(new Integer(1));
list.add(new Integer(3));
ArrayList list2 = ArrayList(list.subList(1,4)); // 인덱스1부터 인덱스4 사이에 저장된 객체 반환
point(list1, list2);
Collections.sort(list1); // list1과 list2를 정렬한다.
Collections.sort(list2); // Collections.sort(List l)
print(list1, list2);
...
}
}
Collection은 인터페이스이고, Collections는 클래스이다.
ArrayList를 생성할 때, 저장할 요소의 개수를 고려해서 실제 저장할 개수보다 약간 여유잇는 크기로 하는 것이 좋다. 생성할 때 지정한 크기보다 더 많은 객체를 저장하면 자동적으로 크기가 늘어나지만 이 과정에서 처리시간이 많이 소요된다.
ArrayList나 Vector 같이 배열을 이용한 자료구조는 데이터를 읽어오고 저장하는 데는 효율이 좋지만, 용량을 변경해야할 때는 새로운 배열을 생성한 후 기존의 배열로부터 새로 생성된 배열로 데이터를 복사해야하기 때문에 상당히 효율이 떨어진다는 단점을 가지고 있다. 그래서 처음에 인스턴스를 생성할 때, 저장할 데이터의 개수를 잘 고려하여 충분한 용량의 인스턴스를 생성하는 것이 좋다.

class Node{
Node next; // 다음 요소의 주소를 저장
Object obj; // 데이터를 저장
}
class Node {
Node next; // 다음 요소의 주소를 저장
Node previous; // 이전 요소의 주소를 저장
Object obj; // 데이터를 저장
}


| 컬렉션 | 읽기(접근시간) | 추가 / 삭제 | 비 고 |
|---|---|---|---|
| ArrayList | 빠르다 | 느리다 | 순차적인 추가삭제는 더 빠름 / 비효율적 메모리 사용 |
| LinkedList | 느리다 | 빠르다 | 데이터가 많을수록 접근성이 떨어짐 |
ArrayList al = new ArrayList(1000000);
for(int i=0;i<100000;i++) al.add(i+"");
LinkedList ll = new LinkedList(al);
for(int i=0;i<1000;i++) ll.add(500, "X");
import java.util.*;
class StackQueueEx{
public stsatic void main(String[] args){
Stack st = new Stack();
Queue q = new LinkedList(); // Queue인터페이스의 구현체인 LinkedList를 사용
st.push("0");
st.push("1");
st.push("2");
q.offer("0");
q.offer("1");
q.offer("2");
System.out.println("= Stack = ");
while(!st.empty()){
System.out.println(st.pop());
}
System.out.println("= Queue =");
while(!q.isEmpty()){
System.out.println(q.poll());
}
}
}
스택의 활용 예 : 수식 계산, 수식 괄호 검사, 워드프로세스의 undo/redo, 웹브라우저의 뒤로/앞으로
큐의 활용 예 : 최근 사용 문서, 인쇄작업 대기 목록, 버퍼(buffer)
import java.util.*;
class PriorityQueueEx{
public static void main(String[] args){
Queue pq = new PriorityQueue();
pq.offer(3); // pq.offer(new Integer(3)); 오토박싱
pq.offer(1);
pq.offer(5);
pq.offer(2);
pq.offer(4);
System.out.println(pq); // pq의 내부 배열을 출력
Object obj = null;
// PriorityQueue에 저장된 요소를 하나씩 꺼낸다.
while((obj = pq.poll())!=null)
System.out.println(obj);
}
}
// 실행 결과
// [1,2,3,4,5]
// 1
// 2
// 3
// 4
// 5
Collection c = new ArrayList(); // 다른 컬렉션으로 변경시 이 부분만 고치면 된다.
Iterator it = c.iterator();
while(it.hasNext()){
System.out.println(it.next());
}
Collection에 없고 ArrayList에만 있는 메서드를 사용하는게 아니라면, Collection타입의 참조변수로 선언하는 것이 좋다. 만일 Collection인터페이스를 구현한 다른 클래스로 바꿔야 한다면 선언문 하나만 변경하면 나머지 코드는 검토하지 않아도 된다.
Map인터페이스를 구현한 컬렉션 클래스는 키(key)와 값(value)을 쌍(pair)으로 저장하고 있기 때문에 iterator()를 직접 호출할 수 없고, 그 대신 keySet()이나 entrySet()과 같은 메서드를 통해서 키와 값을 각각 따로 Set의 형태로 얻어온 후에 다시 iterator()를 호출해야 Iterator을 얻을 수 있다.
int[] arr = new int[5];
Arrays.fill(arr, 9);
Arrays.setAll(arr, ()->(int)(Math.random()*5)+1); // arr=[1,5,2,1,4]
Comparable : 기본 정렬기준을 구현하는데 사용
Comparator : 기본 정렬기준 외에 다른 기준으로 정렬하고자할 때 사용
트리(tree)는 각 노드간의 연결된 모양이 나무와 같다고 해서 붙여진 이름이다.
class TreeNode{
TreeNode left; // 왼쪽 자식노드
Object element; // 객체를 생성하기 위한 참조변수
TreeNode right; // 오른쪽 자식노드
}
이진 검색 트리(binaray search tree)는
- 모든 노드는 최대 두 개의 자식노드를 가질 수 있다.
- 왼쪽 자식노드의 값은 부모노드의 값보다 작고 오른쪽자식노드의 값은 부모노드의 값보다 커야한다.
- 노드의 추가 삭제에 시간이 걸린다.(순차적으로 저장하지 않으므로)
- 검색(범위검색)과 정렬에 유리하다.
- 중복된 값을 저장하지 못한다.
키(key) : 컬렉션 내의 키(key) 중에서 유일해야 한다.
값(value) : 키(key)와 달리 데이터의 중복을 허용한다.
Hashtable은 키(key)나 값(value)으로 null을 허용하지 않지만, HashMap은 허용한다. 그래서 'map.put(null, null);'이나 'map.get(null);'과 같이 할 수 있다.
해싱이란 해시함수(hash function)를 이용해서 데이터를 해시테이블(hash table)에 저장하고 검색하는 기법을 말한다. 해시함수는 데이터가 저장되어 있는 곳을 알려 주기 때문에 다량의 데이터 중에서도 원하는 데이터를 빠르게 찾을 수 있다.
해싱을 구현한 컬렉션 클래스로는 HashSet, HashMap, Hashtable 등이 있다.
해싱에 사용하는 자료구조는 다음과 같이 배열과 링크드 리스트의 조합으로 되어 있다.

저장할 데이터의 키를 해시함수에 넣으면 배열의 한 요소를 얻게 되고, 다시 그 곳에 연결되어 있는 링크드 리스트에 저장하게 된다.

배열은 배열의 크기가 커져도, 원하는 요소가 몇 번째에 있는 지만 알면 아래의 공식에 의해서 빠르게 원하는 값을 찾을 수 있다.
배열의 인덱스가 n인 요소의 주소 = 배열의 시작주소 + type의 size*n
하나의 링크드 리스트에 최소한의 데이터만 저장하려면, 저장될 데이터의 크기를 고려해서 HashMap의 크기를 적절하게 지정해주어야 하고, 해시함수가 서로 다른 키에 대해서 중복된 해시코드의 반환을 최소화해야 한다. 그래야 HashMap에서 빠른 검색시간을 얻을 수 있다.
그래서 해싱을 구현하는 과정에서 제일 중요한 것은 해시함수의 알고리즘이며, 아래의 예처럼 첫 번째 문자를 뽑아서 정수로 반환하여 찾는다.
int hashFunction(String key){
return Integer.parseInt(key.substring(0,1));
}
static Collection synchronizedCollection(Collection c)
static List synchronizedCollection(List list)
static Set synchronizedCollection(Set s)
static Map synchronizedCollection(Map m)
static SortedSet synchronizedCollection(SortedSet s)
static SortedMap synchronizedCollection(SortedMap m)
// 사용법
List syncList = Collections.synchronizedList(new ArrayList(...));
static Collection unmodifiableCollection(Collection c)
static List unmodifiableList(List list)
static Set inmodifiableSet(Set s)
static Map inmodifiableSet(Map m)
static NavigableSet inmodifiableSet(NavigableSet s)
static SortedSet inmodifiableSet(SortedSet s)
static NavigableMap inmodifiableSet(NavigableMap m)
static SortedMap inmodifiableSet(SortedMap m)

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