자바에서 컬렉션
프레임워크(collection framework)
란 다수의 데이터를 쉽고 효과적으로 처리할 수 있는 표준화된 방법을 제공하는 클래스의 집합을 의미합니다
- 데이터를 저장하는 자료 구조와 데이터를 처리하는 알고리즘을 구조화하여 클래스로 구현해 놓은 것이다.
- 컬렉션프레임워크는 인터페이스(interface)를 통해 구현이 되어있다.
List | Set | Map |
---|---|---|
순서가 있는 집합 | 순서가 없는 집합 (집합 주머니안에서 막 꺼내쓰는 느낌) | Entry별로 저장 (key값, value값) |
중복값 허용 O | 중복값 허용 X | key중복값 : 허용X / value중복값 : 허용O |
Vector, ArrayList, LinkedList, Stack, Queue | HashSet, TreeSet | HashMap, TreeMap, HashTable, Properties |
- 연속적이다.
- 배열처럼 인덱스를 참조하므로 검색성능이 좋다.
- 배열은 크기를 미리 지정해주지만, ArrayList는 크기를 지정해주지 않아도 된다.
- 중복값이 허용된다.
특징
- 제네릭(Generic)기반
- 생성할 때 데이터타입을 결정한다(구체화)
ArrayList<String> list = new ArrayList<String>();
제네릭이란 데이터의 타입(data type)을 일반화한다(Generalize)는 것을 의미한다.
제네릭은 클래스나 메소드에서 사용할 내부 데이터 타입을 컴파일 시에 미리 지정하는 방법이다.
제네릭(Generic)의 장점
타입 | 설명 |
---|---|
<T> | Type |
<E> | Element |
<K> | Key |
<V> | Value |
<N> | Number |
배열을 리스트로 변환하는 과정 = 리스트의 초기화과정
List<String> list = new ArrayList<String> (Arrays.asList("일", "월", "화", "목"));
asList
의 마침표 3개 붙어있는 건 갯수가 정해지지 않았을 때 사용한다. (asList("")
asList("", "")
등)
- 인덱스 지정이 없으면 순서대로 저장
- 인덱스 지정도 가능하다.
add("값")
또는add(index, "값")
List<String> list = new ArrayList<String>();
list.add("월");
list.add("화");
list.add(2, "수");
list.add(0, "일");
// [일, 월, 화, 수]
2가지 방법이 있다.
1.boolean remove(Object obj)
: obj 제거. 성공하면 true반환
2.Object remove(int index)
: index위치의 요소 제거. 제거한 요소를 반환
List<String> list = new ArrayList<String> (Arrays.asList("일", "월", "화", "수"));
list.remove(0); // [월, 화, 수]
list.remove("화"); // [월, 수]
System.out.println(list);
배열은 length를 이용하여 배열의 길이를 구하지만, ArrayList는 size()를 이용해 길이를 구한다.
List<String> list = Arrays.asList("일", "월", "화", "수");
int size = list.size();
System.out.println(size); // 4
ArrayList가 요소가 하나도 없어서 비어있는지 확인하는방법은 간단하다.
List<Integer> list = new ArrayList<Integer> ();
boolean result = list.isEmpty(); // true
ArrayList에 넣은 요소를 제거하기 위해서는 clear()
를 사용하면 된다.
List<String> list = Arrays.asList("일", "월", "화", "수");
list.clear();
우리가 배열을 출력할 때 인덱스값이 있기 때문에 인덱스의 i를 이용하여 for문을 이용하여 이렇게 출력을 했었다.
String arr[] = {"일", "월", "화", "수"};
for(int i = 0; i < arr.length; i++) {
System.out.print(arr[i] + " "); // 일 월 화 수
}
List<String> list = Arrays.asList("일", "월", "화", "수");
for(int i = 0, length = list.size(); i < length; i++) {
System.out.print(list.get(i) + " ");
} // 일 월 화 수
ArrayList도 Array의 일종이기 때문에 향상 for문을 이용하여 출력할 수 있다.
List<String> list = Arrays.asList("일", "월", "화", "수");
for(String element : list) {
System.out.print(element + " ");
}
- ArrayList와 동일한 내부 구조를 사용하며, 같은 특징을 갖는다.
- 차이점은 동기화된(Synchronized)메서드로 구성되어 있어 멀티 스레드 환경에서 Thread safe 하다.
- 단일 스레드 환경에서도 동기화를 하기 때문에 비효율적이며, 성능면에서도 동일한 구조를 갖는 ArrayList보다 떨어져 일반적으로 ArrayList를 많이 사용한다.
- 기존코드와의 호환성 문제로 남아있다고 한다.
- 비연속적이다. (집합주머니 안에 들어있다고 생각하면 된다. 막 꺼내쓰는)
- 중복값이 허용되지 않는다.
- Set 컬렉션 클래스 중 가장 많이 사용된다.
- 내부적으로 Hash Algorism을 사용하여 데이터를 관리하기에 검색 속도 성능이 좋다.
- hashCode()와 equals()를 이용해 데이터의 중복을 판단한다.
Set<String> set = new HashSet<String>();
Set<String> set = new HashSet<String>(Arrays.asList("일","월","화","수"));
값을 한번에 지정해줄때는 ArrayList와 같이 Arrays의 asList()
메소드를 활용하면 된다.
Set<String> set = new HashSet<String>();
set.add("일");
set.add("월");
set.add("화");
set.add("수");
set.add("수"); // 중복저장 시도
// [일, 월, 화, 수 ]
중복값이 허용되지 않는다.
Set<String> set = new HashSet<String>();
set.add("일");
set.add("월");
set.add("화");
set.add("수");
set.add("목");
boolean result = set.remove("목");
System.out.println(result); // true
만약 제거할 값이 존재하지 않는다면 false를 출력하고 제거되지 않는다.
ArrayList와 똑같이 size()
메소드를 활용하면된다.
Set<String> set = new HashSet<String>(Arrays.asList("일","월","화","수"));
int size = set.size(); // 4
HashSet은 index가 존재하지 않고, 순차적이지 않으므로 일반 for문을 사용할 수 없다.
그러므로 향상 for문을 사용하여 요소를 출력해보겠다.
Set<String> set = new HashSet<String>(Arrays.asList("일","월","화","수"));
for(String element : set) {
System.out.println(element); // [일, 월, 화, 수 ]
}
HashSet이 마치 순서가 없는 집합 주머니와 같다고 설명했으니 합집합, 차집합, 교집합, 부분집합을 구해보겠다.
Set<Integer> set1 = new HashSet<Integer>(Arrays.asList(1, 2, 3, 4, 5));
Set<Integer> set2 = new HashSet<Integer>(Arrays.asList(3, 4, 5, 6, 7));
Set<Integer> set3 = new HashSet<Integer>(Arrays.asList(6, 7));
set1.addAll(set2); // [1, 2, 3, 4, 5, 6, 7]
set1.removeAll(set2); // [1, 2]
set1.retainAll(set2); // [3, 4, 5]
부분집합은 여부를 판단할 수 있다.
boolean result1 = set1.containsAll(set3);
boolean result2 = set2.containsAll(set3);
System.out.println(result1); // false
System.out.println(result2); // true
- Key와 Value값으로 이루어진 데이터
- Array나 ArrayList의 경우 자동으로 index가 할당되지만, Map의 key값은 자유롭게 만들면 된다.
- Value(값)은 중복값이 있을 수 있으나, Key(키)는 중복값이 있을 수 없다.
- Key와 Value 두개 묶음을
Entry
라고 부른다.
Map<String, String> map1 = new HashMap<String, String>();
// 또는
Map<String, Object> map2 = new HashMap<String, Object>();
실무에서는 String이나 Object 타입을 많이 사용한다고 한다.
Map은 key값과 value값이 따로 있으니까 데이터타입을 두개 지정해주어야 한다.
<Object>
는 모든 데이터타입을 받을 수 있다.
Map은 put()
을 통해 요소추가를 한다.
Map<String, Object> map = new HashMap<String, Object>();
map.put("title", "소나기");
map.put("author", "황순원");
map.put("price", 20000);
Map<String, Object> map = new HashMap<String, Object>();
map.put("title", "소나기");
map.put("author", "황순원");
map.put("price", 20000);
for(Map.Entry<String, Object> entry : map.entrySet()) {
System.out.println(entry.getKey() + ":" + entry.getValue());
}
//author:황순원
//price:20000
//title:소나기
위에 말했다시피 Entry
는 (Key + Value)
를 합쳐 부르는 말이다.
Entry를 기준으로 순회하는 방법 말고도 key
를 이용해 순회하는 방법도 있다.
향상 for문을 사용해서 순회출력을 해보겠다.
Map<String, Object> map = new HashMap<String, Object>();
map.put("title", "소나기");
map.put("author", "황순원");
map.put("price", 20000);
for(String key : map.keySet()) {
System.out.println(key + ":" + map.get(key));
}
//author:황순원
//price:20000
//title:소나기
Map<String, Object> map1 = new HashMap<String, Object>();
map1.put("title", "summer");
map1.put("author", "hisahisijo");
map1.put("price", 10000);
Map<String, Object> map2 = new HashMap<String, Object>();
map2.put("title", "어린왕자");
map2.put("author", "생택쥐베리");
map2.put("price", 12000);
Map<String, Object> map3 = new HashMap<String, Object>();
map3.put("title", "홍길동전");
map3.put("author", "허균");
map3.put("price", 16000);
List<Map<String, Object>> list = new ArrayList<Map<String, Object>>();
list.add(map1);
list.add(map2);
list.add(map3);
for(Map<String, Object> map : list) { // List
for(Map.Entry<String, Object> entry : map.entrySet()) { // Map
System.out.println(entry.getKey() + ":" + entry.getValue());
}
System.out.println();
}
이진트리 (Binary Tree)
1. 모든 노드는 2개의 자식을 가질 수 있다.
2. 작은 값은 왼쪽, 큰 값은 오른쪽에 저장한다.
TreeMap
1. key를 기준으로 왼쪽에 작은 값, 오른쪽에 큰 값이 저장된다.
2. key를 기준으로 자동으로 정렬되면서 저장된다.
3. 크기 비교 및 범위 연산에 적절하다.
Map<Integer, String> map = new TreeMap<Integer, String>();
선언하는 방법은 부모 Map을 선언하고 TreeMap객체를 생성하는 방법으로 같다.
put()
를 이용해서 요소를 추가한다.
map.put(65, "제시카");
map.put(85, "에밀리");
map.put(35, "제임스");
map.put(95, "사만다");
// {35=제임스, 65=제시카, 85=에밀리, 95=사만다}
TreeMap자체에서는 key의 정렬을 자동적으로 해서 출력된다.
TreeMap도 Map이므로 Entry단위로 for순회를 할 수 있다.
for(Map.Entry<Integer, String> entry : map.entrySet()) {
System.out.println(entry.getKey() + ":" + entry.getValue());
}
기본정렬 : 오름차순 정렬
TreeMap은 TreeMap만 사용할 수 있는 메소드가 다수 있으므로 TreeMap 타입으로 생성하는 것이 좋다.
NavigableMap<K,V>
: 지정된 검색 대상에 대해 가장 근접한 일치 항목을 반환하는 탐색 메서드와 함께 확장된 정렬된 맵입니다.
- SortedMap<K,V> 인터페이스를 상속하는 인터페이스이다.
- 구현체는 TreeMap을 사용하면 된다.
// 생성중요
TreeMap<Integer, String> map = new TreeMap<Integer, String>();
map.put(65, "제시카");
map.put(85, "에밀리");
map.put(35, "제임스");
map.put(95, "사만다");
// 정렬변경 : decendingMap() 메소드 호출
// 오름차순 정렬 ↔ 내림차순 정렬
// TreeMap타입으로 써야 TreeMap에서 사용할 수 있는 메소드들을 호출할 수 있다.
NavigableMap<Integer, String> map2 = map.descendingMap();
for(Map.Entry<Integer, String> entry : map2.entrySet()) {
System.out.println(entry.getKey() + ":" + entry.getValue());
//95:사만다
//85:에밀리
//65:제시카
//35:제임스
// 다시 decendingMap() 메소드를 호출하면 오름차순 정렬이 된다.
NavigableMap<Integer, String> map3 = map2.descendingMap();
// key값으로 순회
for(Integer key : map3.keySet()) {
System.out.println(key + ":" + map3.get(key));
}
}