자바 컬렉션 프레임워크는 다양한 데이터 구조를 제공하지만,
특정 요구사항을 충족시키기 위해 사용자 정의 컬렉션을 구현해야 할 때가 있습니다.
이 글에서는 사용자 정의 컬렉션을 구현하는 방법과 모범 사례를 알아보겠습니다.
사용자 정의 컬렉션이 필요한 이유
이유 | 설명 |
---|---|
특수 기능 추가 | 기본 컬렉션이 제공하지 않는 특수 기능이나 동작 필요 시 |
성능 최적화 | 특정 사용 사례에 맞게 성능 개선 필요 시 |
도메인 제약 조건 | 비즈니스 로직이나 도메인 제약 조건을 컬렉션에 적용할 때 |
영속성 구현 | 메모리가 아닌 다른 저장소(파일, DB 등)에 데이터 유지 필요 시 |
사용자 정의 컬렉션 구현 방법
1. 추상 구현 클래스 활용
자바는 사용자 정의 컬렉션 구현을 쉽게 하기 위해 다음과 같은 추상 구현 클래스를 제공합니다.
추상 클래스 | 용도 | 최소 구현 메서드 |
---|---|---|
AbstractCollection | Set이나 List가 아닌 컬렉션 | iterator(), size() |
AbstractSet | Set 구현 | AbstractCollection과 동일 |
AbstractList | 랜덤 액세스 기반 List | get(int), size() |
AbstractSequentialList | 순차 액세스 기반 List | listIterator(), size() |
AbstractMap | Map 구현 | entrySet() |
2. 구현 과정
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
|
// 배열 기반 사용자 정의 리스트 예제
public class MyArrayList<T> extends AbstractList<T> {
private final T[] a;
public MyArrayList(T[] array) {
a = array;
}
@Override
public T get(int index) {
return a[index];
}
@Override
public T set(int index, T element) {
T oldValue = a[index];
a[index] = element;
return oldValue;
}
@Override
public int size() {
return a.length;
}
}
// 사용 예
List<String> myList = new MyArrayList<>(new String[]{“A”, “B”, “C”});
|
cs |
3. Iterable 인터페이스 구현
for-each 루프를 사용할 수 있는 컬렉션을 만들려면 Iterable 인터페이스를 구현해야 합니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
public class CustomCollectionWrapper implements Iterable<String>, Iterator<String> {
private HashMap<String, Object> internalStorage = new HashMap<>();
private int count = 0;
public void add(String key, Object value) {
internalStorage.put(key, value);
}
public Object get(String key) {
return internalStorage.get(key);
}
@Override
public boolean hasNext() {
if (count < internalStorage.size()) {
return true;
}
return false;
}
// Iterator 메서드 구현
// …
}
|
cs |
사용자 정의 컬렉션 구현 예제
1. 커스텀 배열 컬렉션
다음은 동적으로 크기가 조정되는 배열 기반 컬렉션 예제입니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
|
public class CustomArrayCollection {
private Object[] elements;
private int countElements = 0;
// 기본 생성자 – 초기 용량 10
public CustomArrayCollection() {
elements = new Object[10];
countElements = 0;
}
// 요소 추가
public void add(Object element) {
// 용량 확인 및 필요시 확장
if (countElements == elements.length) {
// 용량 두 배로 증가
int newCapacity = elements.length * 2;
Object[] newArray = new Object[newCapacity];
System.arraycopy(elements, 0, newArray, 0, elements.length);
elements = newArray;
}
elements[countElements++] = element;
}
// 크기 반환
public int size() {
return countElements;
}
// 요소 가져오기
public Object get(int index) {
if (index >= countElements) {
throw new IndexOutOfBoundsException();
}
return elements[index];
}
}
|
cs |
2. 특수 목적 컬렉션 예제
특정 도메인 객체를 관리하는 컬렉션 예제
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
|
public class BaseballCardCollection {
private BaseballCard[] baseballCardList;
private int size;
public BaseballCardCollection() {
baseballCardList = new BaseballCard[10];
size = 0;
}
public int size() {
return size;
}
public void addCard(BaseballCard card) {
if (size == baseballCardList.length) {
// 배열 크기 확장
BaseballCard[] newArray = new BaseballCard[baseballCardList.length * 2];
System.arraycopy(baseballCardList, 0, newArray, 0, baseballCardList.length);
baseballCardList = newArray;
}
baseballCardList[size++] = card;
}
public boolean removeCard(BaseballCard card) {
for (int i = 0; i < size; i++) {
if (baseballCardList[i].equals(card)) {
// 카드 제거 및 배열 재정렬
System.arraycopy(baseballCardList, i + 1, baseballCardList, i, size – i – 1);
baseballCardList[––size] = null;
return true;
}
}
return false;
}
}
|
cs |
모범 사례
-
equals()와 hashCode() 재정의: 사용자 정의 객체를 컬렉션에 저장할 때는 이 메서드들을 올바르게 구현해야 합니다.
-
성능 고려: 자주 사용되는 연산에 대해 최적화된 구현을 제공하세요.
-
테스트: 구현한 컬렉션이 예상대로 동작하는지 철저히 테스트하세요.
-
문서화: 컬렉션의 동작과 제약 조건을 명확히 문서화하세요.
사용자 정의 컬렉션을 구현함으로써 애플리케이션의 특정 요구사항에 맞는 데이터 구조를 만들 수 있으며,
이는 코드의 가독성과 성능을 향상시키는 데 도움이 됩니다.