자바의 자료구조는 크게 Collection과 Map으로 나눌 수 있으며, Collection은 List, Set, Queue로 나눌 수 있다.
List, Set, Queue는 상위 인터페이스인 Collection
⇒ Iterable
을 구현하므로 공통적으로 다음과 같은 메서드를 사용할 수 있다.
stream() 및 forEach()를 이용할 수 있으며 데이터 추가 및 제거, 형변환 등의 메서드를 제공한다.
Collection 인스턴스에 저장된 데이터 흐름에 대해 람다를 활용해 함수형으로 처리할 수 있다
iterator()
메소드 호출시 컬랙션 인스턴스의 Iterator객체를 반환하며 Iterator 클래스의 메소드는 다음과 같다.
타입 | 메소드 | 설명 |
---|---|---|
boolean | hasNext() | 가져올 객체가 있다면 true를 return, 없다면 false를 return 한다 |
E | next() | 하나의 객체를 가져온다 |
void | remove() | 객체를 제거한다 |
ArrayList
, LinkedList
, Vector
, Stack
HashSet
, LinkedHashSet
, TreeSet
priorityQueue
, ArrayDeque
HashMap
, LinkedHashMap
, Hashtable
, TreeMap
리스트는 순서를 가지고, 원소의 중복이 허용된다는 특징이 있음
LinkedList 는 노드(Node)와 포인터(Pointer)를 이용하여 만든 리스트
양방향 포인터를 가진다
포인터로 각각의 노드들을 연결하고 있어서, 삽입/삭제가 빠르다
특정 원소를 조회하기 위해서는 첫 노드부터 순회해야하기 때문에 ArrayList에 비해 느리다
참고로 Collections에서 동기화가 되는 컬렉션을 반환하는 정적 메소드 synchronized…()
를 지원한다
Collections.synchronizedList(new ArrayList<>());
Collections.synchronizedSet(new HashSet<>());
Collections.synchronizedMap(new HashMap<>());
스택은 Vector의 하위클래스이고 push()
와 pop()
은 Stack 클래스 내부에 구현한다.
LIFO
(Last-In-First-Out) 특성을 가지는 자료구조이다push
, 나갈때는 pop
이라는 용어를 사용한다peek()
메서드를 사용한다Set
은 집합이라고 부르며, 순서가 없고 원소의 중복을 허용하지 않는다는 특징이 있음
클래스 명 | 특징 |
---|---|
HashSet | Hashing을 이용해서 구현한 컬렉션이다.데이터를 중복 저장할 수 없고, 순서를 보장하지 않는다.equals()나 hashCode()를 오버라이딩해서,인스턴스가 달라도 동일 객체를 구분해 중복 저장을 막을 수 있다. |
TreeSet | 이진탐색트리(Red-Black Tree)의 형태로 데이터를 저장한다. 데이터 추가, 삭제에는 시간이 더 걸리지만, 검색과 정렬이 더 뛰어나다 |
LinkedHashSet | HashSet 클래스를 상속받으며 .삽입된 순서대로 데이터를 관리한다 |
HashSet(Map)은 같은 인스턴스가 아니더라도 동일 객체를 구분하여 중복 저장을 막을 수 있다.
HashSet에 객체를 저장하기 전에, hashCode()를 호출해 해시코드를 얻어내는데, 이미 저장되어 있는 객체들(HashMap Key 객체)의 경우의 해시코드와 비교해서, 만약 동일한 해시코드가 있다면 다시 equals()로 두 객체를 비교해 true가 나오면 동일한 객체로 판단해 중복 저장을 하지 않는 방식이다.
Stack과는 다르게 FIFO(First-In-First-Out) 구조를 가진다. 처음 들어온 원소가 처음으로 나간다는 특징이 있음
LinkedList의 경우 List뿐만 아니라 Queue, Deque를 함께 구현한다
우선순위를 가지는 큐(queue)로 일반적인 큐와는 조금 다르게, 원소에 우선순위를 부여하여 높은 순으로 먼저 반환한다. (이진 트리 구조로 구현되어 있음)
Deque는 양쪽으로 넣고 빼는 것이 가능한 큐 자료구조이다.
ArrayDeque, LinkedList ⇒ Deque 인터페이스의 구현체
method | description. |
---|---|
void clear() | 해당 맵(map)의 모든 매핑(mapping)을 제거함. |
boolean containsKey(Object key) | 해당 맵이 전달된 키를 포함하고 있는지를 확인함. |
boolean containsValue(Object value) | 해당 맵이 전달된 값에 해당하는 하나 이상의 키를 포함하고 있는지를 확인함. |
V get(Object key) | 해당 맵에서 전달된 키에 대응하는 값을 반환함.만약 해당 맵이 전달된 키를 포함한 매핑을 포함하고 있지 않으면 null을 반환함. |
boolean isEmpty() | 해당 맵이 비어있는지를 확인함. |
Set keySet() | 해당 맵에 포함되어 있는 모든 키로 만들어진 Set 객체를 반환함. |
V put(K key, V value) | 해당 맵에 전달된 키에 대응하는 값으로 특정 값을 매핑함. |
V remove(Object key) | 해당 맵에서 전달된 키에 대응하는 매핑을 제거함. |
V replace(K key, V value) | 해당 맵에서 전달된 키에 대응하는 값을 특정 값으로 대체함. |
boolean replace(K key, V oldValue, V newValue) | 해당 맵에서 특정 값에 대응하는 전달된 키의 값을 새로운 값으로 대체함. |
int size() | 해당 맵의 매핑의 총 개수를 반환함. |
boolean remove(Object key, Object value) | 해당 맵에서 특정 값에 대응하는 특정 키의 매핑을 제거함. |
method | description |
---|---|
NavigableMap<K, V> descendingMap() | 모든 맵을 역순으로 반환함. |
NavigableSet<k, > </k,>descendingKeySet() | 모든 키를 역순으로 반환함. |
K firstKey() | 해당 맵에서 현재 가장 작은(첫 번째) 키를 반환함. |
K lastKey() | 해당 맵에서 현재 가장 큰(마지막) 키를 반환함. |
K higherKey(K key) | 해당 맵에서 전달된 키보다 작은 키 중에서 가장 큰 키를 반환함.만약 해당하는 키가 없으면 null을 반환함. |
K lowerKey(K key) | 해당 맵에서 전달된 키보다 큰 키 중에서 가장 작은 키를 반환함.만약 해당하는 키가 없으면 null을 반환함. |
K ceilingKey(K key) | 해당 맵에서 전달된 키와 같거나, 전달된 키보다 큰 키를 반환함. 없으면 null을 반환함. |
K floorKey(K key) | 해당 맵에서 전달된 키와 같거나, 전달된 키보다 작은 키를 반환함. 없으면 null을 반환함. |
SortedMap<K, V> headMap(K toKey) | 해당 맵에서 전달된 키보다 작은 키로 구성된 부분만을 반환함. |
SortedMap<K, V> subMap(K fromKey, K toKey) | 해당 맵에서 fromKey(포함)부터 toKey(미포함)까지로 구성된 부분만을 반환함. |
SortedMap<K, V> tailMap(K fromKey) | 해당 맵에서 fromKey와 같거나, fromKey보다 큰 키로 구성된 부분만을 반환함. |
한 번에 하나의 스레드만 객체에 접근하도록 허용한다.
synchronized object는 여러 스레드에 의해 동시에 조작될 수 없다.
package java.util;
public class Vector<E> extends AbstractList<E>implements List<E>, RandomAccess, Cloneable, java.io.Serializable {
...
public synchronized boolean add(E e) {
modCount++;
add(e, elementData, elementCount);
return true;
}
public synchronized int size() {
return elementCount;
}
...
}
package java.util;
public class Hashtable<K,V>extends Dictionary<K,V>implements Map<K,V>, Cloneable, java.io.Serializable {
...
@SuppressWarnings("unchecked")
public synchronized V get(Object key) {
Entry<?,?> tab[] = table;
int hash = key.hashCode();
int index = (hash & 0x7FFFFFFF) % tab.length;
for (Entry<?,?> e = tab[index] ; e != null ; e = e.next) {
if ((e.hash == hash) && e.key.equals(key)) {
return (V)e.value;
}
}
return null;
}
public synchronized int size() {
return count;
}
...
}
package java.util;
public class Collections {
...
public static <T> List<T> synchronizedList(List<T> list) {
return (list instanceof RandomAccess ?
new SynchronizedRandomAccessList<>(list) :
new SynchronizedList<>(list));
}
public static <K,V> Map<K,V> synchronizedMap(Map<K,V> m) {
return new SynchronizedMap<>(m);
}
public static <T> Set<T> synchronizedSet(Set<T> s) {
return new SynchronizedSet<>(s);
}
static class SynchronizedList<E>extends SynchronizedCollection<E>implements List<E> {
@SuppressWarnings("serial") // Conditionally serializable
final List<E> list;
public E get(int index) {
synchronized (mutex) {return list.get(index);}
}
public void add(int index, E element) {
synchronized (mutex) {list.add(index, element);}
}
public E remove(int index) {
synchronized (mutex) {return list.remove(index);}
}
...
}
...
}
List<String> list = Collections.synchronizedList(new ArrayList<String>());
SynchronizedList 내부 클래스를 보면 모든 메서드의 각 synchronized 블록에서 mutex 객체를 공유한다. 한 스레드가 synchronized 블록에 들어가는 순간 다른 메서드의 syncrhonized 블록이 lock에 걸리기 때문에 다른 스레드들은 모두 동기화 메서드를 이용하지 못하고 blocking 상태가 된다.
읽기와 쓰기 동작 시 모두 인스턴스 자체에 잠금이 걸린다.