[Java] 컬렉션 프레임웍 (Collections Framework)
Collection : 데이터 그룹
Framework : 표준화된 프로그래밍 방식
다양한 데이터 타입들을 하나의 그룹으로 모아 처리할 수 있게 해 준다.
List Set Map 이 세 가지가 컬렉션 프레임웍의 핵심 인터페이스이다. List와 Set를 구현한 컬렉션 클래스들은 서로 많은 공통부분이 있어 공통부분을 Collection 인터페이스로 정의하고 List와 Set는 Collection 인터페이스를 상속받도록 설계했다.
그러니까 정리하면 Collection이 맨 위에 있고 그 밑에 List와 Set, 그리고 List와 Set를 상속받는 친구들이 밑에 있는 형식이다. List로 구현한 ArrayList와 LinkedList, Stack같은 친구들은 Collection과 List의 메서드는 물론 자신의 메서드까지 사용할 수 있다.
List : 순서가 있고 데이터의 중복을 허용함.
ArrayList / LinkedList / Stack / Vector 등..
Set : 순서가 없고 데이터의 중복을 허용하지 않음.
HashSet / TreeSet 등..
Map : 키와 값의 쌍으로 이루어진 데이터 집합. 순서가 없고 키는 중복을 허용하지 않고, 값은 중복을 허용함.
HashMap / TreeMap / Hashtable / Properties 등..
HashMap은 Python 언어의 dictionary와 비슷하다.
Collection 인터페이스에 여러 가지 메서드들이 정의돼있는데 외우기보다는 코드 짜 보면서 익숙해지자.
ArrayList
기존의 Vector 클래스를 개선했다. List 인터페이스를 구현하기 때문에 순서가 있고 데이터의 중복을 허용한다.
ArrayList의 코드를 살펴보면, Object타입의 배열이 선언된 걸 확인할 수 있다. 이 배열 덕분에 모든 종류의 객체를 담을 수 있다. 배열의 크기도 동적으로 관리된다. 기본으로 설정된 크기는 10이고, 더 이상 저장할 공간이 없으면 보다 큰 새로운 배열을 생성하고 기존 배열의 내용을 새로운 배열로 복사하고 저장한다. (단, 메모리 낭비가 심하니 되도록이면 배열 크기를 넉넉히 선언하자.)
역시 여러 가지 메서드들이 있는데, 외우지 말고 코드 짜 보면서 익히자.
ArrayList의 원소들끼리는 순서가 존재하기 때문에 한 원소가 삭제되면 그 뒤에 있는 순서가 싹 바뀐다. 빈 공간을 채우기 위해 나머지 요소들이 자리이동을 수행한다. 이때문에, 리스트에 있는 원소를 조작할 때는 이를 고려해 작업해야 한다.
ArrayList에서 Capacity(용량)과 Size(크기)는 전혀 다른 개념이다.
용량은 이 리스트가 수용할 수 있는 객체의 수를 의미하고, 크기는 들어와 있는 객체의 수를 의미한다.
trimToSize() 메서드를 통해 용량과 크기를 바꿔주면, 그에 맞는 용량과 크기를 가지는 리스트를 새로 생성하고 값을 복사한 후 참조변수가 새롭게 생성한 리스트를 참조하도록 한다. 그리고 이전에 참조하던 리스트는 garbage collector에 의해 버려진다.
remove() 메서드를 통해 리스트의 요소를 삭제할 때, 마지막 인덱스의 요소를 삭제하는 경우는 그냥 null처리해주고 끝나지만, 그게 아니라면, 삭제할 원소 위의 원소들을 복사하고 한 칸 씩 붙여서 붙여넣고 마지막 원소를 null로 처리해주는 복잡한 과정을 거친다.
LinkedList
위와 같은 단점을 극복하기 위해 새로운 자료구조인 LinkedList가 고안됐다.
일반적인 리스트는 데이터가 연속적으로 존재하지만, LinkedList는 불연속적으로 존재하는 데이터를 연결한 형태로 구성돼있다.
이 리스트에서 데이터를 삭제하는 경우는, 삭제하고자 하는 요소의 이전요소가 삭제하고자 하는 요소의 다음 요소를 참조하도록 변경해 주면 된다. ArrayList에서처럼 데이터를 복사하는 과정이 없어 빠르게 처리할 수 있다.
하지만 이 리스트는 연결된 방향이 일방통행처럼 한 방향으로 이어져 있어, 이전 요소에 대한 접근은 어렵다. 이걸 보완하기 위해 double LinkedList가 등장했다. (말 그대로 양방향으로 이어진 리스트)
여기에 더 발전해 double circular LinkedList도 등장했는데, 마지막 원소 다음의 원소가 맨 처음 원소가 되도록 프로그래밍된 리스트이다.
이론적인 얘기에 너무 파묻히지 말고 아 이런게 있구나 정도만 생각하고 LinkedList를 어떻게 활용하는지에 초점을 맞춰 공부하자.
역시 여러 가지 메서드들이 있는데, 외우지 말고 코드 짜면서 익히자.
ArrayList와 LinkedList 중 뭘 어떨 때 써야 할까?
1. 순차적으로 추가 / 삭제하는 경우는 ArrayList를 사용하는 것이 더 합리적이다. (크기가 여유롭게 설정돼있음을 가정.)
순차적으로 삭제하는 경우는 뒤에서부터 삭제하는걸 말한다.
2. 중간 데이터를 추가 / 삭제하는 경우는 LinkedList를 사용하는게 더 합리적이다.
LinkedList의 특성을 생각해보자.
Stack과 Queue
스택과 큐 역시 리스트를 통해 구현하기 때문에 자료의 중복을 허용하고 순서가 있다.
스택은 마지막에 저장한 데이터를 먼저 꺼내는 LIFO(Last In First Out)구조이고
큐는 처음 저장한 데이터랑 가장 먼저 꺼내는 FIFO(First In First Out)구조이다.
스택은 밑이 닫힌 통에 물건을 넣고 꺼내는 이미지를 상상하면 편하다.
큐는 밑이 뚫린 통에 물건을 넣는건 상상해보자.
스택을 구현할 때는 ArrayList와 같은 배열기반의 컬렉션 클래스를 사용하는게 합리적이고, 큐를 구현할 때는 LinkedList처럼 데이터의 추가와 삭제가 쉬운 컬렉션 클래스를 이용하는게 합리적이다. (스택과 큐의 정의에 입각해 생각해보자.)
스택은 Stack st = new Stack(); 처럼 바로 구현할 수 있지만
큐는 Queue q = new LinkedList(); 처럼 LinkedList를 사용해서 구현해야 한다.
자바에서 스택은 Stack 클래스로 구현해 제공하고 있지만, Queue는 인터페이스로만 정의해 놓고 별도의 클래스로 제공하고 있지는 않다.
여기서 잠깐 헷갈릴 수 있는 배열과 리스트의 차이를 짚고 가자.
여러 타입으로 지정할 수 있는 배열은 int[] arr = new int[N]; 같은 형식으로 작성되는데 List와 배열은 큰 차이가 있다.
배열은 배열의 원소들을 다루는 도구라고 생각한다면, 리스트는 요소들을 다루는 건 동일하지만 내용물을 뽑아내고 버리고 다시 추가하고.. 즉 가방같은 역할을 한다.
배열의 원소도 추가하거나 뺄 수는 있지만, 리스트와는 다르다.
두 차이를 제대로 이해하고 프로그램을 작성하도록 하자.
Queue 에도 PriorityQueue 처럼 우선순위를 지정하고 우선순위대로 먼저 출력하게 하는 기능을 하는 Queue의 구현체, Deque 처럼 양쪽에서 추가와 삭제를 수행할 수 있는 기능을 하는 Queue의 구현체 등 다양한 Queue의 구현체가 있는데, 일단은 아~ 이런 것들이 있군 까지만 생각하자.
Iterator
컬렉션에 저장된 각 요소에 접근하는 기능을 한다.
배열의 경우는 인덱스로 데이터에 접근할 수 있는데, 모든 컬렉션이 인덱스로 접근할 수 있는건 아니다. 그래서 Iterator를 사용한다.
Iterator 인터페이스는 Collection에 정의돼있고 List나 Set를 구현하는 컬렉션은 iterator() 메서드가 각 컬렉션의 특징에 알맞게 작성돼있다. Set는 순서가 상관없는 컬렉션인데 이에 맞춰서 이터레이터가 정의돼있다.
공부할 수록 자바의 객체지향적 설계가 정말 대단하다는 생각이 든다.
hasnext() : 읽어 올 요소가 남아있는지 확인함 있으면 true 없으면 false
next() : 다음 요소를 읽어옴 읽어오기만하고 요소를 조작하진 않음
ArrayList list1 = new ArrayList();
위의 코드에서 첫 번째 ArrayList는 참조변수의 타입이고 뒤의 ArrayList는 클래스를 구현한다.
Collection c = new ArrayList();
위와 같이 코드를 짜면 ArrayList를 LinkedList로 바꿀 때 선언문 하나만 변경하면 나머지 코드는 검토하지 않아도 돼 코드 수정이 편하다.
Iterator를 상속받아서 ListIterator를 구현한 경우도 있는데 이 클래스는 Iterator가 단방향으로만 이동할 수 있는 단점을 보완해 양방향으로의 이동이 가능하도록 프로그래밍돼있다. 앞에서 봤던 double Linked List를 생각하면 좋을듯하다.
Arrays
리스트를 배우기 전에도 잘 써왔던 그냥 배열이다 (int[] arr; 이거임)
sort() 배열을 오름차순으로 정렬한다.
binarySearch() 배열에서 특정 값이 저장된 위치를 찾는다. sort()가 선행되어야 한다.
toString() 배열의 모든 요소를 문자열로 출력해준다. 다차원 배열을 대상으로 사용할 때는 deepToString() 메서드를 사용한다.
equals() 두 배열에 저장된 모든 요소를 비교한다. 역시 다차원 배열을 대상으로 할 때는 deepToEquals() 메서드를 사용한다.
처음 자바를 공부할 때 배웠듯 배열에 저장된 배열의 주소를 비교하는 문제 때문에 2차원 이상의 배열에서는 다른 메서드를 사용한다. (String을 비교할 때도 마찬가지임)
== 를 사용해서 비교하면 주소를 비교하고, equals() 메서드를 사용해서 비교하면 1차원의 요소를 하나하나 비교한다.
asList() 배열을 리스트로 변환할 때 사용한다.
Comparator와 Comparable
Arrays.sort()를 호출하면 컴퓨터가 알아서 배열을 정렬하는것처럼 보이는데, 사실 Character클래스의 Comparable을 구현해서 정렬된거다. Comparator와 Comparable은 모두 인터페이스이다.
Comparable은 클래스에 종속적으로 동작한다. 즉, int와같이 기본적으로 대소비교가 가능한, 순서가 있는 클래스에 대해서 정렬을 진행할 때 Comparable인터페이스를 활용해 정렬한다.
즉, 어떤 클래스를 작성할 때 그 클래스에 대한 순서를 정의할 때는 Comparable을 implement해서 compareTo() 메서드를 구현해서 작성해야 한다
Comparable은 클래스에 정의된 순서를 바탕으로 정렬한다면, Comparator는 다른 기준으로 새로운 순서를 만들어 정렬하고 싶을 때 사용한다. 새로 클래스를 만들고 Comparator를 implement해서 compare() 메서드를 정의해주면 된다.
Comparator는 compare 메서드를 통해서 인자들을 비교하고 Comparable은 compareTo 메서드를 통해서 인자들을 비교한다. 선언형태와 이름이 약간 다르지만 두 객체를 비교하는 목적은 같다. 해당 오브젝트를 기준으로 인자로 받는 요소보다 순서가 앞이면 -1, 같으면 0 순서가 뒤면 1을 반환하는 방식으로 프로그래밍 돼 있다. 이렇게 기본적으로는 오름차순으로 정렬하는데, 만약 내림차순으로 새로운 정렬기준을 만들어 정렬하고싶으면 Comparator를 통해 1과 -1을 반환하는 경우를 바꿔서 코드를 작성하면 된다.
즉, Comparable은 기본 정렬기준을 구현할 때 사용하고 Comparator는 이미 정의된 순서를 다르게 정의하고 싶을 때 사용한다.
sort(T[] a, Comparator<? super T> c)
sort메서드의 일부인데, <>와 T, ? 표현은 일단 넘어가자.
정렬하고싶은 배열을 첫 번째 인자로 받아오고, 두 번째 인자로 Comparator를 받아온다. Comparator를 implement한 크클래스를 두 번째 인자로 넣어주면 된다.
Hashset
Hash를 이용해서 Set을 관리하는걸 의미한다.
'Programming Language > Java' 카테고리의 다른 글
[Java] 스트림 (Stream) (0) | 2021.10.25 |
---|---|
[Java] 람다식 (Lambda expression) (0) | 2021.10.25 |
[Java] 열거형 (enums) (0) | 2021.10.18 |
[Java] 지네릭스 (Generics) (0) | 2021.10.17 |
[Java] Scanner클래스의 nextLine()과 next() 사용 시 주의점 (0) | 2021.10.05 |
댓글
이 글 공유하기
다른 글
-
[Java] 스트림 (Stream)
[Java] 스트림 (Stream)
2021.10.25 -
[Java] 람다식 (Lambda expression)
[Java] 람다식 (Lambda expression)
2021.10.25 -
[Java] 열거형 (enums)
[Java] 열거형 (enums)
2021.10.18 -
[Java] 지네릭스 (Generics)
[Java] 지네릭스 (Generics)
2021.10.17