Collections Framework (feat.Map은 Collection 인가?)

김운채·2023년 5월 20일
0

Java

목록 보기
2/11
post-thumbnail

이 포스트에서는 Map은 Collection 이라고 할 수 있을까? 에 대한 답을 찾기 위한 디벨롭의 과정을 다룬다.

Collections Framework

컬렉션 프레임워크는 데이터군을 저장하는 클래스들을 표준화한 설계를 뜻한다.

  • 컬렉션(Collection) - 다수의 데이터 집합 (자료구조)
  • 프레임워크(Framework) - 표준화된 프로그래밍

JDK1.2 이전까지는 Vector, Hashtable, Properies 와 같은 컬렉션 클래스, 다수의 데이터를 저장할 수 있는 클래스들을 서로 다른 각자의 방식으로 처리해야했으나,
JDK1.2부터 컬렉션 프레임워크가 등장하면서 다양한 종류의 컬렉션 클래스가 추가되고 모든 컬렉션 클래스를 표준화된 방식으로 다룰 수 있도록 체계화되었다.

Collections Framework의 특징

  1. 컬렉션 클래스들이 데이터를 다룰 때 그 데이터는 기본적으로 객체만 가능하다.

따라서 char, int, float와 같은 기본형은 사용할 수 없고 대신 Primitive Type을 Collection에 포함시키기 위해서는 Wrapper Class를 사용하여야 한다.
하지만, 오토박싱(auto boxing)과 오토언박싱(auto unboxing)으로 인해 사용자는 마치 기본형을 다룰 수 있는 것처럼 사용할 수 있다.

예제를 보자.

ArrayList<Integer> arrayList = new ArrayList<>();

// 값을 넣음
arrayList.add(3);

// 첫번째 요소를 꺼내옴
arrayList.get(0);

값을 꺼내올 때 사실은 Integer 객체의 3을 꺼내온다는 것이다.
다만 사용이 편리하도록 autoboxing이 될 뿐이다.

오래전👵(Java 5 이전)에는 오토박싱이 없었고, 예를 들어 Integers 컬렉션에 대해 단순히 add(5)를 호출할 수 없었다.
그 당시에는 이러한 원시 값을 해당 래퍼 클래스로 수동으로 변환하고 컬렉션에 저장해야 했다.

현재 autoboxing을 사용하면 ArrayList.add(3) 를 쉽게 수행할 수 있지만 내부적으로 Java는 valueOf() 메서드를 사용하여 ArrayList에 저장하기 전에 기본 값을 Integer로 변환한다.

즉, 오토박싱을 통해 기본 데이터형을 컬렉션에 직접 대입하여 저장해도 컴파일러가 자동으로 Wrapper 클래스로 변환해준다.

arrayList.get(0) 처럼 저장된 값을 얻어올 때에도 객체화된 데이터를 기본 데이터형으로 바로 얻어올 수 있는 데, 이 경우 언박싱(unboxing)이라는 용어를 사용한다.

  1. 제네릭(generics) 형태

컬렉션과 관련된 인터페이스 또는 클래스가 정의되어 있는 코드를 살펴보면 아래와 같이 제네릭(generics) 형태로 구현되어 있는 것을 확인할 수 있다. 따라서 사용자는 사용하고자 하는 데이터 타입을 지정하여 사용할 수 있다.

public interface List<E> extends Collection<E> {   ...   }

Collections Framework의 장점

컬렉션 프레임워크는 다수의 데이터를 다루는 데 필요한 다양하고 풍부한 클래스들을 제공하기 때문에 프로그래머의 짐을 상당히 덜어주고 있으며, 또한 인터페이스와 다형성을 이용한 객체지향적 설계를 통해 표준화되어잇기 때문에(데이터 삽입, 탐색, 정렬 등 편리한 API 를 다수 제공) 사용법을 익히기에도 편리하고 재사용성이 높은 코드를 작성할 수 있다는 장점이 있다.

또한 컬렉션은 생성할 때 특정 용량을 할당할 필요가 없으며, 데이터가 추가 및 제거될 때 자동으로 크기가 맞춰지기 때문에, 배열이 가진 단점을 보완할 수 있다.

배열은 생성할 때 크기가 정해지고 그 크기를 넘어가면 데이터를 저장할 수 없다.
또한 데이터가 비어있으면 메모리가 낭비된다는 단점이 있다.

정리하자면,

  • List, Queue, Set, Map 등의 인터페이스를 제공하고, 이를 구현하는 클래스를 제공하여 일관된 API 를 사용할 수 있다.
  • 가변적인 저장 공간을 제공한다. 고정적인 저장 공간을 제공하는 배열에 대비되는 특징이다.
  • 자료구조, 알고리즘을 구현하기 위한 코드를 직접 작성할 필요 없이, 이미 구현된 컬렉션 클래스를 목적에 맞게 선택하여 사용하면 된다.
  • 제공되는 API 의 코드는 검증되었으며, 고도로 최적화 되어있다.

Collections Framework의 종류

컬렉션 인터페이스는 그룹이 2개로 나뉜다.

  • java.util.Collection : 가장 기본적인 인터페이스
  • java.util.Map : 컬렉션으로 조작할 수 있는 collection-view operations을 가진다.

① 순서가 있는 목록인 List형
② 순서가 중요하지 않은 목록인 Set형
③ 먼저 들어온 것이 먼저 나가는 Queue형
④ KEY-VALUE의 형태로 저장되는 Map형

Map은 Collection 인가?

위의 사진을 보면 의문이 드는게 있다. Java의 컬렉션 프레임워크라면 크게 list, set, map으로 알고 있는데, list 와 set은 컬렉션 인터페이스를 상속받고 있는데 map 은 남일처럼 동떨어져 있다.🤷‍♀️

Collections Framework Overview에서는 "다른 컬렉션 인터페이스는 java.util.Map 기반으로 한다. 그리고 실제 컬렉션이 아니다." 라고 한다.

또한 Collections API에 대한 FAQ 에서는 다음과 같은 질문과 답변이 있다.

Why doesn't Map extend Collection?

This was by design. We feel that mappings are not collections and collections are not mappings. Thus, it makes little sense for Map to extend the Collection interface (or vice versa).

If a Map is a Collection, what are the elements? The only reasonable answer is "Key-value pairs", but this provides a very limited (and not particularly useful) Map abstraction. You can't ask what value a given key maps to, nor can you delete the entry for a given key without knowing what value it maps to.

Map 인터페이스는 구조상 특징이 달라 List , Queue , Set 과 달리 Collection 를 상속받지 않는다.

List나 Set같은 컬렉션 인터페이스들은 단일 데이터를 처리하여 공통된 부분이 있다.
하지만 Map은 키(key)와 값(value)이 쌍을 이루며 처리하기 때문에 다른 형태로 컬렉션을 다룬다고 볼 수 있다.

만약 Map 과 컬렉션 이 둘의 연관관계를 억지로 만들게 되면 부자연스러운 인터페이스가 만들어질 수밖에 없다.

즉, 컬렉션 프레임워크에서 컬렉션 데이터 그룹을 다루는데 필요한 기능을 가진 인터페이스는 List, Set, Queue, Map 4가지가 있는데,
여기서 List, Set, Queue의 공통된 부분을 가져와서 Collection 인터페이스를 정의하였다.
그렇기 때문에 Map도 컬렉션이라고 말하기도 하지만 Java에서는 실질적으로 컬렉션이라고 보진 않는다라고 할 수 있겠다.

주요 인터페이스의 특징

각각은 블로그의 다른 글에 정리해 놨으니 읽어보길!

Collection 인터페이스의 주요 메서드

  • boolean add(E e) : 현재 컬렉션에 데이터 객체 e를 추가
  • boolean addAll(Collection<? extends E> c) : 현재 컬렉션에 컬렉션 c의 모든 데이터를 추가
  • boolean contains(Object o) : 현재 컬렉션에 객체 o의 포함 여부를 반환
  • boolean containsAll(Collection<?> c) : 현재 컬렉션에 컬렉션 c의 모든 데이터가 포함되어있는지 여부를 반환
  • boolean remove(Object o) : 현재 컬렉션에서 객체 o를 삭제
  • boolean removeAll(Collection<?> c) : 현재 컬렉션에서 컬렉션 c와 일치하는 데이터를 삭제
  • boolean retainAll(Collection<?> c) : 현재 컬렉션에서 컬렉션 c와 일치하는 데이터만 남기고 나머지는 삭제
  • void clear( ) : 현재 컬렉션의 모든 데이터를 삭제
  • int size( ) : 현재 컬렉션에 포함된 데이터 개수를 반환
  • boolean isEmpty( ) : 현재 컬렉션이 비어있는지 여부를 반환
  • Iterator iterator( ) : 현재 컬렉션의 모든 요소에 대한 iterator를 반환
  • Object[ ] toArray( ) : 현재 컬렉션에 저장된 데이터를 Object 배열로 반환
  • T[ ] toArray(T[ ] a ) : 현재 컬렉션에 저장된 데이터를 배열 a에 담고 배열 a를 반환

참고자료:
https://olivejua-develop.tistory.com/66
https://ssdragon.tistory.com/20
https://joooootopia.tistory.com/13
velog.io/@seculoper235/1.-자바의-Collection에-대해
https://hudi.blog/java-collection-framework-1/

0개의 댓글