
📚 이것이 자바다 [개정판]
- 자바는 자료구조를 바탕으로 관련된 인터페이스와 클래스를 java.util 패키지에 포함시켜 놓았다
- 이 모든 것을 총칭하여 컬렉션 프레임워크라고 부른다
- 몇 가지 인터페이스를 통해 다양한 컬렉션 클래스를 이용할 수 있도록 설계되어 있다

- 순서를 유지하고 저장한다
- 중복 저장이 가능하다
| 기능 | 메소드 | 설명 |
|---|---|---|
| 객체 추가 | add(E e) | 맨 끝에 객체 추가 |
| add(int index, E element) | 주어진 인덱스에 객체 추가 | |
| set(int index, E element) | 주어진 인덱스의 객체를 새로운 객체로 변경 | |
| 객체 검색 | contains(Object o) | 객체의 저장여부 확인 |
| get(int index) | 주어진 인덱스에 저장된 객체 리턴 | |
| isEmpty() | 컬렉션이 비어 있는지 확인 | |
| size() | 저장된 전체 객체 수 리턴 | |
| 객체 삭제 | clear() | 저장된 모든 객체 삭제 |
| remove(int index) | 주어진 인덱스에 저장된 객체 삭제 | |
| remove(Object o) | 주어진 객체 삭제 |
List<E> list = new ArrayList<E>(); // E에 지정된 타입의 객체만 저장 List<E> list = new ArrayList<>(); // E에 지정된 타입과 동일하면 생략 가능 List list = new ArrayList(); // 모든 타입의 객체 저장
- 객체를 추가하면 내부 배열에 객체가 저장된다 (null 저장 가능)
- 객체 자체를 저장하는 것이 아닌 객체의 번지를 저장한다
- 동일한 객체를 중복하여 저장하는 경우 동일한 번지가 저장된다
- 제한 없이 객체를 추가할 수 있다는 것이 일반 배열과 차이점이다
- 객체를 추가하거나 제거하면 인덱스 번호가 1씩 당겨지거나 밀려난다
따라서, 빈번한 객체 삭제와 삽입이 일어나는 곳에서 사용하지 않는 것이 좋다list.remove(2); list.remove(2); // 2번 인덱스를 삭제하면 기존의 3번이 2번으로 변경되므로 // 다시 2번 인덱스를 제거할 수 있음
List<E> list = new Vector<E>(); // E에 지정한 타입의 객체만 저장 List<E> list = new Vector<>(); // E에 지정된 타입과 동일하면 생략 가능 List list = new Vector(); // 모든 타입의 객체 저장
- ArrayList와 동일한 내부 구조를 가지고 있으나 Vector는 동기화된 메소드로 구성되어 있다
- 멀티 스레드가 동시에 Vector() 메소드를 실행할 수 없다는 뜻으로 멀티 스레드 환경에서 안전하게 객체 변경이 가능하다
List<E> list = new LinkedList<E>(); List<E> list = new LinkedList<>(); List list = new LinkedList();
- ArrayList와 사용 방법은 동일하지만 내부 구조는 완전히 다르다
- LinkedList는 인접 객체를 체인처럼 연결하여 관리한다
- 특정 위치에서 객체가 변경되면 바로 앞뒤 링크만 변경하면 되므로 빈번한 객체 변경이 일어나는 곳에서 좋은 성능을 발휘한다
- 순서를 유지하지 않고 저장
- 중복 저장 안됨
| 기능 | 메소드 | 설명 |
|---|---|---|
| 객체 추가 | add(E e) | 주어진 객체를 저장하면 true, 중복 객체면 false 리턴 |
| 객체 검색 | contains(Object o) | 주어진 객체가 저장되어 있는지 여부 |
| boolean containsValue(Object value) | 주어진 값이 있는지 여부 | |
| isEmpty() | 컬렉션이 비어 있는지 확인 | |
| Iterator iterator() | 저장된 객체를 한 번씩 가져오는 반복자 리턴 | |
| size() | 저장되어 있는 전체 객체 수 리턴 | |
| 객체 삭제 | clear() | 저장된 모든 객체를 삭제 |
| remove(Object o) | 주어진 객체를 삭제 |
Set<E> set = new HashSet<E>(); Set<E> set = new HashSet<>(); Set set = new HashSet();
- 다른 객체라도 hashCode() 리턴값이 같고 equals()가 true를 리턴하면 동일한 객체로 판단한다
- Set 컬렉션은 인덱스로 객체를 검색해서 가져오는 메소드가 없어서 객체를 한 개씩 반복해서 가져와야 한다
hasNext()가져올 객체가 있으면 true를 리턴하고 없으면 false를 리턴next()컬렉션에서 하나의 객체를 가져옴remove()next()로 가져온 객체를 Set 컬렉션에서 제거
// 1. for문을 이용하는 방법
Set<E> set = new HashSet<>();
for(E e : set) {
...
}
// 2. iterator() 메소드로 반복자를 얻는 방법
Set<E> set = new HashSet<>();
Iterator<E> iterator = set.iterator();
while(iterator.hasNext()) {
E e = iterator.next();
}
TreeSet<E> treeSet = new TreeSet<E>(); TreeSet<E> treeSet = new TreeSet<>(); // 검색 관련 메소드가 TreeSet에만 정의되어 있으므로 TreeSet 타입으로 대입
- 이진 트리 기반 : 루트 노드에서 시작하여 각 노드에 2개의 노드를 연결한 트리 형태의 구조
- 자동 정렬되며 부모 노드 객체와 비교하여 낮으면 왼쪽, 높으면 오른쪽 자식 노드에 저장한다
- 검색 기능을 강화시킨 컬렉션이다
- Key-Value로 구성된 Entry 객체를 저장한다 (key와 value 모두 객체이다)
- Key는 중복 저장할 수 없고, 동일한 키로 값을 저장하면 새로운 값으로 변경된다
- Value는 중복 저장할 수 있다
| 기능 | 메소드 | 설명 |
|---|---|---|
| 객체 추가 | put(K key, V value) | 키와 값을 추가하고 저장되면 값을 리턴 |
| 객체 검색 | containsKey(Object key) | 주어진 키가 있는지 여부 |
| containsValue(Object value) | 주어진 값이 있는지 여부 | |
| Set(Map.Entry<K,V>> entrySet() | 키와 값의 쌍으로 구성된 모든 Map.Entry 객체를 Set에 담아 리턴 | |
| get(Object key) | 주어진 키의 값을 리턴 | |
| isEmpty() | 컬렉션이 비어있는지 여부 | |
| Set keySet() | 모든 키를 Set 객체에 담아 리턴 | |
| size() | 저장된 키의 총 수를 리턴 | |
| Collection values() | 저장된 모든 값 Collection에 담아 리턴 | |
| 객체 삭제 | clear() | 모든 Map.Entry(키와 값)를 삭제 |
| remove(Object key) | 주어진 키와 일치하는 Map.Entry를 삭제하고 삭제되면 값을 리턴 |
Map<K, V> map = new HashMap<K, V>(); Map<String, Integer> map = new HashMap<String, Integer>(); Map<String, Integer> map = new HashMap<>();키로 사용할 객체의 hashCode() 메소드 리턴값이 같고 equals() 메소드가 true를 리턴할 경우, 동일한 키로 보고 중복 저장을 허용하지 않는다
// key set 컬렉션을 얻고 반복해서 키와 값 얻기
Set<String> keySet = map.keySet();
Iterator<String> keyIterator = keySet.iterator();
while(keyIterator.hasNext()) {
String k = keyIterator.next();
Integer v = map.get(k);
System.out.println(k + ":" + v);
}
// 엔트리 Set 컬렉션을 얻고, 반복해서 키와 값을 얻기
Set<Entry<String, Integer>> entrySet = map.entrySet();
Iterator<Entry<String, Integer>> entryIterator = entrySet.iterator();
while (entryIterator.hasNext()) {
Entry<String, Integer> entry = entryIterator.next();
String k = entry.getKey();
Integer v = entry.getValue();
System.out.println(k + ":" + v);
}
System.out.println();
Map<String, Integer> map = new Hashtable<String, Integer>(); Map<String, Integer> map = new Hashtable<>();
- HashMap와 동일한 내부 구조를 가지고 있으나 Hashtable은 동기화된 메소드로 구성되어 있다
- 멀티 스레드가 동시에 Hashtable의 메소드를 실행할 수 없다는 뜻으로 멀티 스레드 환경에서 안전하게 객체 변경이 가능하다
💡 Properties
- Hashtable의 자식 클래스이므로 Hashtable의 특징을 그대로 가지고 있다
- key와 value를 String 타입으로 제한한 컬렉션이다
- 주로 프로퍼티 파일을 읽을 때 사용한다
// properties 파일 예시 driver=oracle.jdbc.OracleDirver username=scott password=tiger// Properties 사용 예시 Properties properties = new Properties(); properties.load(Xxx.class.getResourceAsStream("파일명.properties"));
TreeMap<K, V> treeSet = new TreeMap<K, V>(); TreeMap<K, V> treeSet = new TreeMap<>(); // 검색 관련 메소드가 TreeMap에만 정의되어 있으므로 TreeMap 타입으로 대입
- 이진 트리 기반
- TreeSet과 다른 점은 key-value가 저장된 Entry를 저장한다는 점이다
- 키를 기준으로 자동 정렬되며 부모 키 값과 비교해서 낮은 것은 왼쪽, 높은 것은 오른쪽 자식 노드에 Entry 객체를 저장한다
- 검색 기능을 강화시킨 컬렉션이다
Comparable 예제
- TreeSet에 저장되는 객체와 TreeMap에 저장되는 키 객체는 저장과 동시에 오름차순으로 정렬
- 단, 객체가 Comparable 인터페이스를 구현하고 있는 경우에 정렬이 된다
- Comparable 인터페이스에는 compareTo() 메소드가 정의되어 있어 메소드 재정의가 필요하다
- Comparable 비구현 객체를 저장하고 싶은 경우 Comparator 비교자를 제공하면 된다
// Comparable
class Person implements Comparable<Person> { // 제네릭 타입 클래스 명시
public String name;
public int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public int compareTo(Person o) {
if (age < o.age)
return -1; // 작으면 -1
else if (age == o.age)
return 0; // 같으면 0
else
return 1; // 크면 1
}
}
public class ComparableEx {
public static void main(String[] args) {
TreeSet<Person> treeSet = new TreeSet<Person>();
treeSet.add(new Person("홍길동", 45));
treeSet.add(new Person("김길동", 26));
treeSet.add(new Person("차길동", 30));
for (Person person : treeSet) {
System.out.println(person.name + ":" + person.age);
}
}
}
Comparator 예시 코드
// Comparator
class Fruit {
public String name;
public int price;
public Fruit(String name, int price) {
this.name = name;
this.price = price;
}
}
class FruitComparator implements Comparator<Fruit> {
@Override
public int compare(Fruit o1, Fruit o2) {
if(o1.price < o2.price) return -1;
else if(o1.price == o2.price) return 0;
else return 1;
}
}
public class ComparatorEx {
public static void main(String[] args) {
TreeSet<Fruit> treeSet = new TreeSet<Fruit>(new FruitComparator());
treeSet.add(new Fruit("포도", 3000));
treeSet.add(new Fruit("수박", 10000));
treeSet.add(new Fruit("딸기", 6000));
for(Fruit fruit : treeSet) {
System.out.println(fruit.name + ":" + fruit.price);
}
}
}
Stack<E> stack = new Stack<E>(); Stack<E> stack = new Stack<>();
- Last In First Out 후입선출 - 나중에 넣은 객체가 먼저 빠져 나가는 구조
- push(E item) : 주어진 객체를 스택에 넣는다
- pop() : 스택의 맨 위 객체를 빼낸다
Queue<E> queue = new Queue<E>(); Queue<E> queue = new Queue<>();
- First In First Out 선입선출 - 먼저 넣은 객체가 먼저 빠져 나가는 구조
- offer(E e) : 주어진 객체를 큐에 넣는다
- poll() : 큐에서 객체를 빼낸다