컬렉션(collection) : 여러 객체(데이터)를 모아 놓은 것
프레임워크(framework) : 표준화, 정형화된 체계적인 프로그래밍 방식
컬렉션 프레임워크(collections framework)
컬렉션 클래스(collection class)
: 저장 순서가 있는 데이터의 집합. 데이터의 중복을 허용한다.
예시) 대기자 명단
구현 클래스 : ArrayList, LinkedList, Stack, Vector 등
get()
: 읽기, set()
: 변경, indexOf()
: 검색, sort()
: 정렬: 저장 순서를 유지하지 않는 데이터의 집합. 데이터의 중복을 허용하지 않는다.
예시) 소수의 집합, 육식동물의 집합
List와 특징이 정반대
구현 클래스 : HashSet, TreeSet 등
Collection 인터페이스의 메서드와 동일
집합과 관련된 메서드들(Collection에 변화가 있으면 true, 아니면 false를 반환)
메서드 | 설명 |
---|---|
boolean addAll(Collection c) | 저장된 Collection의 객체들을 Collection에 추가한다.(합집합) |
boolean containsAll(Collection c) | 저장된 Collection의 객체들이 Collection에 포함되어 있는지 확인한다.(부분집합) |
boolean removeAll(Collection c) | 저장된 Collection에 포함된 객체들을 삭제한다.(차집합) |
boolean retainAll(Collection c) | 저장된 Collection에 포함된 객체만을 남기고 나머지는 Collection에서 삭제한다.(교집합) |
: 키(key)와 값(value)의 쌍(pair)으로 이루어진 데이터의 집합. 저장 순서는 유지하지 않으며, 키는 중복을 허용하지 않고 값은 중복을 허용한다.
예시) 아이디(key)-패스워드(value)
구현 클래스 : HashMap, TreeMap, Hashtable, Properties 등
LinkedHashMap : 저장 순서가 있음
Hashtable과 HashMap은 유사. 동기화 유무 차이(Hashtable 동기화O, HashMap 동기화X)
put()
, putAll()
: 추가, containsKey()
, containsValue()
, get()
: 검색, remove()
: 삭제
Set keySet()
: 키만 읽기, Collection values()
: 값만 읽기, Set entrySet()
: Entry(키-값) 읽기
Collection values()
: 리턴 타입이 Collection이기 때문에 순서와 중복이 상관 없음
Set keySet()
, Set entrySet()
: 리턴 타입이 Set이기 때문에 순서X, 중복X
add()
: 추가, contains()
: 검색, remove()
: 삭제, clear()
: 전체 삭제ArrayList는 기존의 Vector를 개선한 것으로 구현 원리와 기능적으로 동일. ArrayList는 동기화X, Vector는 동기화O
List 인터페이스를 구현하므로, 저장 순서가 유지되고 중복을 허용한다.
데이터의 저장 공간으로 배열을 사용한다.(배열 기반)
ArrayList() : 기본 생성자
ArrayList(Collection c) : 컬렉션끼리 변환할 때 사용
ArrayList(int initialCapacity) : 배열의 길이 지정
boolean add(Object o) : 객체를 추가. 성공하면 true, 실패하면 false
void add(int index, Object element) : 저장 위치(index) 지정 가능
boolean addAll(Collection c) : 컬렉션의 모든 요소를 추가
boolean addAll(int index, Collection c) : 컬렉션의 모든 요소를 저장 위치(index)에 추가
boolean remove(Object o) : 객체 삭제
Object remove(int index) : index의 객체 삭제
boolean removeAll(Collection c) : 지정한 컬렉션의 객체들을 삭제
void clear() : 모든 객체 삭제
int indexOf(Object o) : 객체가 몇 번째 index에 있는지 반환, 못 찾으면 -1 반환
int lastindexOf(Object o) : 끝에서부터 객체를 찾음.
boolean contains(Object o) : 객체가 존재하는지 물어봄. 있으면 true, 없으면 false
Object get(int index) : index 위치의 객체를 읽음
Object set(int index, Object element) : index의 위치에 있는 객체를 변경
List subList(int from, int to) : from부터 to까지의 인덱스를 뽑아내서 새로운 리스트를 만듬(to는 포함x)
Object[] toArray() : ArrayList의 객체 배열을 반환
boolean isEmpty() : ArrayList가 비어있으면 true, 아니면 false
void trimToSize() : 빈 공간을 제거
int size() : 저장된 객체의 개수를 반환
ArrayList list1 = new ArrayList(10);
list1.add(new Integer(5));
list1.add(4);
list1.add(new Integer(2));
list1.add(new Integer(0));
list1.add(new Integer(1));
list1.add(new Integer(3))
ArrayList list2 = new ArrayList(list1.subList(1,4));
list1 = [5, 4, 2, 0, 1, 3]
list2 = [4, 2, 0]
ArrayList list1 = new ArrayList(10);
: 기본 길이(용량, Capacity)가 10인 ArrayList 생성
list1.add(4);
: 이렇게 해도 추가가 된다. 컴파일러가 오토 박싱을 통해서 list1.add(new Integer(4));
이렇게 자동으로 변환해준다.
list1.subList(1,4)
: 인덱스 1부터 4 전까지의 객체를 뽑아 새로운 ArrayList를 만든다. 인덱스 4는 포함x
ArrayList list2 = new ArrayList(list1.subList(1,4));
: ArrayList(Collection c)
생성자를 통해 list2를 만들어준다.
Collections.sort(list1);
Collections.sort(list2);
list1 = [0, 1, 2, 3, 4, 5]
list2 = [0, 2, 4]
Collections
는 유틸 클래스이다. 헷갈리지 말자. Collection
이 인터페이스System.out.println("list1.containsAll(list2) = "
+ list1.containsAll(list2));
list1.containsAll(list2) = true
list2.add("B");
list2.add("C");
list2.add(3, "A");
list2 = [0, 2, 4, A, B, C]
list2.add("B");
: list2의 맨 뒤에 추가된다.list2.add(3, "A");
: 인덱스가 3인 위치에 "A"가 추가된다.list2.set(3, "AA");
list2 = [0, 2, 4, AA, B, C]
list2.set(3, "AA");
: 인덱스가 3인 위치의 객체가 "AA"로 변경된다.list1.add(0, "1");
int result1 = list1.indexOf("1");
int result2 = list1.indexOf(1);
list1 = [1, 0, 1, 2, 3, 4, 5]
result1 = 0
result2 = 2
list1.remove(1);
list1 = [1, 1, 2, 3, 4, 5]
list1.remove(new Integer(1));
라고 해줘야 한다.System.out.println("list1.retainAll(list2) = "
+ list1.retainAll(list2));
list1.retainAll(list2) = true
list1 = [2, 4]
list2 = [0, 2, 4, AA, B, C]
list1.retainAll(list2)
: list1에서 list2와 겹치는 부분만 남기고 나머지는 모두 삭제for (int i = list2.size() - 1; i >= 0; i--) {
if (list1.contains(list2.get(i)))
list2.remove(i);
}
list1 = [2, 4]
list2 = [0, AA, B, C]
list2에서 list1에 포함된 객체들을 삭제
ArrayList인 alphabet에서 "C"를 삭제하는 과정
ArrayList<String> alphabet = new ArrayList<>(7); // 배열의 길이 7
alphabet.add("A");
alphabet.add("B");
alphabet.add("C");
alphabet.add("D");
alphabet.add("E");
alphabet.remove(2); // 인덱스가 2인 위치의 "C" 삭제
1. 삭제할 데이터의 다음 데이터부터 마지막 데이터까지 한 칸씩 위로 복사해서 삭제할 데이터를 덮어쓴다.
System.arraycopy(alphabet, 3, alphabet, 2, 2);
2. 데이터가 모두 한 칸씩 이동했으므로 마지막 데이터를 null로 변경한다.
alphabet[size - 1] = null;
3. 데이터가 삭제되어 데이터의 개수가 줄었으므로 size의 값을 감소시킨다.
size--;
[참고] 마지막 데이터를 삭제하는 경우는 1번의 과정(배열 복사)이 필요 없다.
결국, 배열의 복사가 일어나는 1번 과정이 부담이 많이 가는 과정이기 때문에 되도록이면 1번 과정이 없는 것이 좋다.
for(int i = 0; i < alphbet.size(); i++) {
alphabet.remove(i);
}
for(int i = alphbet.size() -1; i >= 0; i--) {
alphabet.remove(i);
}
배열의 장단점
장점 : 배열은 구조가 간단하고 데이터를 읽는데 걸리는 시간(접근 시간, access time)이 짧다.
단점1 : 크기를 변경할 수 없다.
단점2 : 비순차적인 데이터의 추가, 삭제에 시간이 많이 걸린다.
배열의 단점을 복사하기 위해 나온게 LinkedList 이다.
class Node {
Node next;
Object data;
}
next : 다음 노드의 주소
data : 데이터 값
데이터 삭제 : 단 한번의 참조 변경만으로 가능
데이터 추가 : 한번의 Node 객체 생성과 두번의 참조 변경만으로 가능
LinkedList : 연결리스트. 데이터 접근성이 나쁘다.
더블리 링크드 리스트(doubly linked list) : 이중 연결 리스트. 접근성 향상
더블리 써큘러 링크드 리스트(doubly circular linked list) : 이중 원형 연결 리스트
순차적으로 데이터 추가/삭제 : ArrayList가 빠르다.
비순차적으로 데이터 추가/삭제 : LinkedList가 빠르다.
접근 시간(access time) : ArrayList가 빠르다.
인덱스가 n인 데이터의 주소 = 배열의 주소 + n * 데이터 타입의 크기
ArrayList : 배열의 복사를 줄이기 위해 배열을 크게 설정하게 되면 비효율적인 메모리 사용이 된다.(배열 기반 - 연속적)
LinkedList : 데이터가 많을수록 접근성이 떨어진다.(연결 기반 - 불연속적)
스택(Stack) : LIFO(Last In First Out) 구조. 마지막에 저장된 것을 제일 먼저 꺼낸다. ArrayList로 구현하는게 유리
큐(Queue) : FIFO(First In First Out) 구조. 제일 먼저 저장한 것을 제일 먼저 꺼낸다. LinkedList로 구현하는게 유리
메서드 | 설명 |
---|---|
boolean empty() | Stack이 비어있는지 알려준다. |
Object peek() | Stack 맨 위에 저장된 객체를 반환한다. pop()과 달리 객체를 꺼내지는 앟는다.(비어있는 Stack이면 EmptyStackException 발생) |
Object pop() | Stack 맨 위에 저장된 객체를 꺼낸다.(비어있는 Stack이면 EmptyStackException 발생) |
Object push(Object item) | Stack에 item을 저장한다. |
int search(Object o) | Stack에서 주어진 객체(o)를 찾아서 그 위치를 반환한다. 못 찾으면 -1 반환(배열과 달리 위치가 0이 아닌 1부터 시작한다.) |
Stack 클래스가 있기 때문에 Stack st = new Stack();
사용 가능
메서드 | 설명 |
---|---|
boolean add(Object o) | 저장된 객체를 Queue에 추가한다. 성공하면 true, 저장 공간이 부족하면 IlliegalStateException 발생 |
Object remove() | Queue에서 객체를 꺼내 반환한다. 비어있으면 NoSuchElementException 발생 |
Object element() | 삭제 없이 요소를 읽어온다. peek()과 달리 Queue가 비어있으면 NoSuchElementException 발생 |
boolean offer(Object o) | Queue에 객체를 저장한다. 성공하면 true. 실패하면 false 반환. 예외 발생x |
Object poll() | Queue에서 객체를 꺼내서 반환한다. 비어있으면 null 반환. 예외 발생x |
Object peek() | 삭제 없이 요소를 읽어온다. Queue가 비어있으면 null 반환. 예외 발생x |
Queue는 인터페이스이기 때문에 Queue를 구현한 클래스로 객체를 생성해야 한다.
Queue q = new LinkedList();
q.offer("A");
스택의 활용 예 : 수식 계산, 수식 괄호 검사, 워드프로세서의 undo/redo, 웹브라우저의 뒤로/앞으로
Stack<String> st = new Stack<>();
String expression = "((2+4)*7)/2";
System.out.println("expression = " + expression);
try {
for (int i = 0; i < expression.length(); i++) {
if (expression.charAt(i) == '(')
st.push(expression.charAt(i) + "");
else if (expression.charAt(i) == ')')
st.pop();
}
if (st.empty())
System.out.println("괄호가 일치합니다.");
else
System.out.println("괄호가 불일치합니다.");
} catch (EmptyStackException e) {
System.out.println("괄호가 불일치합니다. 예외 발생");
}
expression = ((2+4)*7)/2
괄호가 일치합니다.
expression = ((2+4)*7/2
괄호가 불일치합니다.
expression = ((2+4)*7))))/2
괄호가 불일치합니다. 예외 발생
st.pop()
이 실행되서 EmptyStackExcpetion
예외가 발생큐의 활용 예 : 최근 사용 문서, 인쇄 작업 대기 목록, 버퍼(buffer)
public static void save(String input) {
if (!"".equals(input))
q.offer(input);
if (q.size() > MAX_SIZE)
q.poll();
}
if (!"".equals(input))
: 빈 문자열이 입력 받은 것과 같지 않다면 동작. !input.equals("")
이렇게 적어도 되지만 이러면 input이 null 일때 NullPointerException
이 발생한다. !"".equals(input)
이렇게 작성해주면 input이 null 이어도 예외가 발생하지 않고 빈 문자열인 ""
도 null이 될수 없기 때문에 이렇게 작성해준다.final int SIZE = list.size();
for (int i = 0; i < SIZE; i++) {
System.out.println((i + 1) + ". " + list.get(i));
}
i < list.size()
이렇게 지정해주면 for 문을 돌 때 마다 list.size()를 호출하게 된다. for 문을 돌 때 size()는 바뀌지 않으므로 final int SIZE = list.size();
로 size()를 한번만 알아내고 for 문을 돌 때는 i < SIZE
이렇게 상수를 이용하는게 좋은 코드이다.컬렉션에 저장된 데이터를 접근하는데 사용되는 인터페이스
메서드 | 설명 |
---|---|
boolean hasNext() | 읽어올 요소가 남아있는지 확인한다. 있으면 true, 없으면 false 반환 |
Object next() | 다음 요소를 읽어온다. |
컬렉션에 저장된 요소들을 읽어오는 방법을 표준화한 것
List list = new ArrayList(); // 다른 컬렉션으로 바꿀 때 이 부분만 고치면 된다.
Iterator it = list.iterator();
while (it.hasNext())
System.out.println(it.next());
public interface Collection {
...
public Iterator iterator();
...
}
iterator()
메서드는 Collection
인터페이스 안에 정의되어 있기 때문에 List 인터페이스와 Set 인터페이스에 전부 구현 되어있는 메서드이다.import java.util.*;
class Ex11_5 {
public static void main(String[] args) {
Collection<Integer> c = new ArrayList<>();
// Collection<Integer> c = new LinkedList<>();
c.add(1);
c.add(2);
c.add(3);
c.add(4);
Iterator<Integer> it = c.iterator();
while (it.hasNext()) {
System.out.println(it.next());
}
}
}
Collection<Integer> c = new ArrayList<>();
: 참조변수 타입을 Collecion으로 하게 되면 컬렉션을 LinkedList 로 바꿔도 그 다음의 코드를 바꾸거나 검토할 필요가 없게 된다. 참조 변수 타입이 Collecion 이기 때문에 그 다음의 코드는 Collection에 있는 메서드를 사용했음을 의미하고 따라서 객체를 다른 컬렉션으로 바꿔도 동작한다는 의미이기 때문이다.Map에는 iterator()가 없다.
Set keySet()
, Set entrySet()
, Collection values()
로 키 또는 값을 Set, Collecion 형식으로 바꾼 후에 iterator() 사용
짝수의 값(value)을 전부 더하는 예제
HashMap<Character, Integer> hashMap = new HashMap<Character, Integer>(){{
put('a', 1);
put('b', 4);
put('c', 6);
put('d', 9);
}};
int sum = 0;
Collection<Integer> c = hashMap.values();
Iterator<Integer> it = c.iterator();
while (it.hasNext()) {
int i = it.next();
if (i % 2 == 0)
sum += i;
}
System.out.println("sum = " + sum);
sum = 10
HashMap<Character, Integer> hashMap = new HashMap<Character, Integer>(){{
put('a', 1);
put('b', 4);
put('c', 6);
put('d', 9);
}};
int sum = 0;
Set<Character> keySet = hashMap.keySet();
Iterator<Character> it = keySet.iterator();
while (it.hasNext()) {
int i = hashMap.get(it.next());
if (i % 2 == 0)
sum += i;
}
System.out.println("sum = " + sum);
sum = 10
HashMap<Character, Integer> hashMap = new HashMap<Character, Integer>(){{
put('a', 1);
put('b', 4);
put('c', 6);
put('d', 9);
}};
int sum = 0;
Set<Map.Entry<Character, Integer>> entrySet = hashMap.entrySet();
Iterator<Map.Entry<Character, Integer>> it = entrySet.iterator();
while (it.hasNext()) {
int i = it.next().getValue();
if (i % 2 == 0)
sum += i;
}
System.out.println("sum = " + sum);
sum = 10
getValue()
: Entry 객체의 Value 객체를 반환