
배열의 한계
- 배열 사이즈가 정적이기 때문에 추가 연산시 자리가 부족할 수 있다.
- 배열의 중간 원소 삽입이나 삭제가 힘들다.
- 배열 사이즈가 정적임을 대비해 너무 크게 할당할 경우 메모리 낭비가 심해진다.
이러한 한계를 극복한 것이 컬렉션이다.
컬렉션
- 배열을 동적으로 변경해서 사용자가 사용할 수 있도록 만든 프레임워크이다.
- 제너릭으로 되어있고, 객체 생성시 타입을 지정한다.
- 동일 타입의 데이터를 저장할 수 있다.
- 장점
- 일관된 API
- 프로그래밍 노력 감소
- 프로그램 속도 및 품질 향상
클래스/ 인터페이스 구조도

주요 인터페이스
List<E>
- 순서 유지
- 인덱스 존재
- 원소 중복 가능
- ArrayList
- Array를 이용해서 내용 유지
- 배열 크기 부족해지면 50%로 늘림
- 연속적 주소 할당
- 검색 성능 👍🏻
- 연속적으로 주소 할당되어 있으므로 검색 성능이 좋음
- 추가 / 삭제 성능 👎🏻
- 중간 원소 추가나 삭제가 일어날 경우 앞으로 댕겨주거나 뒤로 밀어주는 작업 필요
- not synchronized
- Vector
- JDK 1.0
- 현재 가치는 JRE 1.0, JRE 1.1 환경을 대상으로 구현한 Java 애플리케이션이 잘 동작할 수 있도록 하위 호환성을 제공하기 위함
- synchronized
- "Re-sizable Array" + "Synchronization”
- 이러한 디자인보다는 크기 조정할 수 있는 배열 ArrayList이 있고 동기화할 때마다 Collections.synchronizedList()을 사용하는 게 좋음
- 모든 메소드 동기화 → 시간 오래걸림, 애플리케이션 성능 저하 👎🏻
- 모든 작업을 동기화하는 것보다 동기화할 작업 집합이 필요
- 대신에 *Collections.synchronizedList,* ConcurrentQueue 사용
- Array 이용해서 내용 유지
- 배열 크기 부족해지면 2배로 늘림
- LinkedList
- 비연속적 주소 할당
- 양방향 연결 리스트 doubly linked list 로 구현
- next_node 주소 뿐만 아니라 prev_node 주소도 가지고 있음
- single linked list보다 검색 성능이 향상
- 추가 / 삭제 성능 👍🏻
- 추가나 삭제가 일어날 경우 변경되는 노드만 다시 연결
- 검색 성능 👎🏻
- 인덱스가 없기에 특정 요소에 접근하기 위해서는 순차 탐색 필요
- 📌 ArrayList는 검색이 많은 경우에 사용하고 LinkedList는 삽입/삭제 시 사용
- ArrayList
Set<E>
- 집합 개념
- 기본적으로 순서 유지 보장 ❌
- 인덱스라는 개념이 없음
- 원소 중복 불가
- hashCode() 와 equals() 가 true 이면 동일한 객체라고 판단
- 구현 클래스
- hashing
- hash function
- 데이터를 효율적으로 관리하기 위해 임의의 길이 데이터를 고정된 길이의 데이터로 맵핑하는 함수
- h(key) = value
- key - 맵핑 전 데이터
- hash value - 매핑 후 해시값
- hashing - 매핑하는 과정 자체
- 데이터를 효율적으로 관리하기 위해 임의의 길이 데이터를 고정된 길이의 데이터로 맵핑하는 함수
- hashing 과정
- hash table이라는 가상공간을 만들어 기억공간을 할당하고 hash function을 이용하여 레코드 키에 대한 hash table 내의 home address를 계산한 후 주어진 레코드를 해당 기억장소에 저장하거나 검색 작업을 수행하는 방식
- hash function (”타깃 코더”) = home address
- hash table해쉬 테이블
- 해시 충돌
- 해쉬함수는 해쉬값보다 보통 많은 키값을 해쉬값으로 변환
- 서로 다른 두 개의 키에 대해 동일한 해시값 반환해시 충돌 (이름을 0~15 사이의 정수값으로 매핑하는 해시 함수의 예)
- 해결 방법
- Direct-address table
- 키의 전체 개수와 동일한 크기의 버킷을 가진 해시테이블
- 해시 충돌 발생 ❌
- 전체 키가 실제 사용하는 키보다 훨씬 많은 경우 사용하지 않는 키들을 위한 공간까지 마련해야 하는 탓에 메모리 효율성 저하
- 해시함수를 사용하여 key를 해시값으로 매핑하고 이 해시값을 index 또는 주소 삼아 데이터의 value을 키와 함께 저장하는 자료구조를 해시테이블
- 보통은 해시테이블 크기 (𝑚)가 실제 사용하는 키 (𝑛)보다 적은 해시테이블 사용
- 데이터가 많고 메모리 등 리소스 문제도 생기기 때문
- load factor(α) = 𝑛/𝑚
- 해시테이블의 한 버킷에 평균 몇 개의 키가 매핑되는가
- 1보다 크면 해쉬 충돌
- Chaining 자바
- 충돌시 연결 리스트에 추가
- 중복된 해시값이 있는 경우 해당 슬롯을 연결 리스트로 저장
- 연결 리스트로 인해 최악의 경우 수행 시간이 O(n)
- 트리로 구성하여 시간 복잡도 줄일 수 있음 O(logn) 자바
- Open Addressing
- 충돌 발생시 해시함수로 얻은 주소가 아닌 다른 주소 공간에 데이터 저장
- 오픈 어드레싱은 충돌을 피하기 위한 다른 방법으로 key를 해시 테이블에 직접 저장
- 구현 간편하고 검색도 미세하게 빨라짐
- 성능이 좋지만 슬롯이 80% 차게 되면 급격하게 성능저하가 일어남
- 언어별 해결방법 채택언어 방식
C++ 개별 체이닝 Java 개별 체이닝 Go 개별 체이닝 Ruby 오픈 어드레싱 Python 오픈 어드레싱
- Direct-address table
- 장점
- 적은 리소스로 많은 데이터를 효율적으로 관리하기 위함
- 하드디스크나 클라우드에 존재하는 무한에 가까운 데이터들을 유한한 개수의 해시값으로 매핑함으로써 작은 크기의 캐쉬 메모리로 프로세스를 관리할 수 있음
- 인덱스에 해시값을 사용함으로써 모든 데이터를 살피지 않아도 검색과 삽입/삭제를 빠르게 수행할 수 있음 𝑂(1)
- 해시함수는 언제나 동일한 해시값을 리턴하고 해당 색인만 알면 해시테이블의 크기에 상관없이 데이터에 대단히 빠르게 접근할 수 있음
- 인덱싱은 계산이 간단한 함수로 작동하기 때문에 효율적
- 문제점
- 해쉬는 rehashing 될때 비정상적인 시간 걸릴 수 있음
- 해쉬는 페이지 폴트 발생 가능
- 페이지 폴트 - 프로그램이 자신의 주소 공간에는 존재하지만 시스템 RAM에는 현재 없는 데이터나 코드에 접근 시도하였을 경우 발생하는 현상
- 페이지간 전환이 많아지면 느려짐
- 해쉬는 효율적인 lookup을 위해 버켓 안의 모든 요소 스왑 가능
- 해쉬 테이블 리사이징 가능 - 성능 갑자기 다운됨
- 해쉬는 메모리 낭비가 있을 수 있음
- hash function
- HashSet
- 해쉬 테이블에 원소 저장
- 삽입 / 삭제 / 조회 연산 → 평균 $O(1)$ 보장
- 삽입시 최악의 경우 $O(n)$
- 해쉬 테이블 리사이징 할 경우
- 원소 삽입 순서 유지 ❌
- 동일한 객체 저장하지 않음
- LinkedHashSet해쉬 테이블연결 리스트
- 해쉬 테이블과 연결 리스트가 결합된 형태
- 원소 삽입 순서 유지
- TreeSet
- 크기 순서 유지
- 속도는 HashSet 보다 느림
- 이진 검색 트리 기반
- 이진 검색 트리 중 [레드-블랙 트리](<https://ko.wikipedia.org/wiki/%EB%A0%88%EB%93%9C-%EB%B8%94%EB%9E%99_%ED%8A%B8%EB%A6%AC>) 이용
- red-black tree
- self-balancing binary search tree
- 검색 / 정렬 👍🏻
- 레드-블랙 트리는 roughly하게 balaned하므로 $O(\log {n})$
- 추가 / 삭제 오래 걸림 👎🏻
- 최대 3번의 트리 회전이 필요함
- 복잡하지만 복잡도는 여전히 $O(\log {n})$
- 해쉬에 비해 일관된 성능을 가지지만 범위 검색 불가능
- 해쉬는 rehashing 될때 비정상적인 시간 걸릴 가능성 존재
- 연속된 삽입을 통해 지역성 유지가 쉬움
- 더 적은 I/O 발생
- 이진 검색 트리 중 [레드-블랙 트리](<https://ko.wikipedia.org/wiki/%EB%A0%88%EB%93%9C-%EB%B8%94%EB%9E%99_%ED%8A%B8%EB%A6%AC>) 이용
- 하나의 노드는 데이터, 왼쪽 자식, 오른쪽 자식로 구성
- 원소 삽입 순서 유지 ❌
- 입력된 순서대로 값을 저장하지 않음
- 크기 순서 유지
- hashing
Map<K, V>
- Key, Value 쌍으로 이루어진 구조
- 기본적으로 순서 유지 보장 ❌
- 인덱스라는 개념이 없음
- 키 중복 불가 / 원소 중복 가능
- 구현 클래스
- HashMap
- JDK 2
- 삽입 순서 유지되지 않음
- 해쉬 테이블에 키 값을 저장
- 삽입 / 삭제 / 조회 연산 → 평균 $O(1)$ 보장
- 삽입시 최악의 경우 $O(n)$
- 해쉬 테이블 리사이징 할 경우
- not synchronized
- 보조 해쉬 함수를 이용
- 보조 해쉬 함수 사용하지 않는 Hashtable에 비해 해쉬 충돌이 덜 발생
- 보조 해쉬 함수 구현
- static final int hash(Object key) { int h; return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16); }
- Hashtable 구현에는 거의 변화가 없지만 HashMap은 지속적으로 개선됨
- 웹 개발시 활용 🕸
- 웹 애플리케이션 서버의 경우에는 HTTPRequest가 생성될 때마다 여러 개의 HashMap이 생성
- 수많은 HashMap 객체가 1초도 안 되는 시간에 생성되고 GC 대상이 됨
- JSON ↔️ HashMap
- LinkedHashMap해쉬 테이블연결 리스트
- 해쉬 테이블과 연결 리스트가 결합된 형태
- 원소 삽입 순서 유지
- TreeMapTreeMap 구조
- 크기 순서 유지
- 이진 검색 트리 기반
- 이진 검색 트리 중 [레드-블랙 트리](<https://ko.wikipedia.org/wiki/%EB%A0%88%EB%93%9C-%EB%B8%94%EB%9E%99_%ED%8A%B8%EB%A6%AC>) 이용
- red-black tree
- self-balancing binary search tree
- 해쉬에 비해 일관된 성능을 가지지만 범위 검색 불가능
- 해쉬는 rehashing 될때 비정상적인 시간 걸릴 가능성 존재
- 연속된 삽입을 통해 지역성 유지가 쉬움
- 더 적은 I/O 발생
- 검색 / 정렬 👍🏻
- 레드-블랙 트리는 roughly하게 balaned하므로 $O(\log {n})$
- 추가 / 삭제 오래 걸림 👎🏻
- 최대 3번의 트리 회전이 필요함
- 복잡하지만 복잡도는 여전히 $O(\log {n})$
- 이진 검색 트리 중 [레드-블랙 트리](<https://ko.wikipedia.org/wiki/%EB%A0%88%EB%93%9C-%EB%B8%94%EB%9E%99_%ED%8A%B8%EB%A6%AC>) 이용
- 하나의 노드는 key, value, 왼쪽 자식, 오른쪽 자식으로 구성
- 원소 삽입 순서 유지 ❌
- 입력된 순서대로 값을 저장하지 않음
- HashMap
Stack<E>
- 책이 쌓이는 것처럼 📚 나중에 넣은 객체가 먼저 빠져나가는 자료구조
- 입력 1 → 2 → 3 → 4
- 출력 4 → 3 → 2 → 1
- Last In, First Out (LIFO)
- 예시
- Stack 메모리
- 할당 순서 a → b → c → i
- 해제 순서 i → c → b → a
- int a = 3; int b = 5; int c = 7; for(int i = 0; ... ){ }
- Stack 메모리
- Stack<E> stack = new Stack<>();
- Stack은 클래스
- Stack은 Vector를 상속받아 만듦
Queue<E>
- 사람이 줄서있는 것처럼 먼저 넣은 객체가 먼저 빠져나가는 자료구조
- 입력 1 → 2 → 3 → 4
- 출력 1 → 2 → 3 → 4
- First In, First Out (FIFO)
- Queue<E> queue = new LinkedList<>();
- Queue는 인터페이스
- 실제 구현은 LinkedList 통해 구현
- Queue는 구현 방법을 명시하고 있지 않다는 점에서 ADT 관점
'Java' 카테고리의 다른 글
| [Java] 스트림 (0) | 2022.10.31 |
|---|---|
| 토이프로젝트 후 느낀점 (0) | 2022.10.29 |
| [Java] 배열(심화) (1) | 2022.10.05 |
| [Java] 자바 입출력 (0) | 2022.10.02 |
| [Java] 예외 처리 (0) | 2022.10.02 |