컬렉션 프레임워크 소개

컬렉션 프레임워크는 JDK에서 가장 널리 사용되는 API입니다. 작업 중인 애플리케이션이 무엇이든 언젠가는 메모리에 데이터를 저장하고 처리해야 할 가능성이 높습니다.

데이터 구조의 역사는 컴퓨팅 자체만큼이나 오래 전으로 거슬러 올라갑니다. 컬렉션 프레임워크는 Java가 발명되기 훨씬 전에 개발된 메모리에 데이터를 저장, 구성 및 액세스하는 방법에 대한 개념을 구현한 것입니다. 컬렉션 프레임워크는 이 작업을 매우 효율적인 방식으로 수행합니다.

컬렉션 프레임워크는 1998년 Java SE 2에서 처음 도입되었으며 그 이후 두 차례에 걸쳐 재작성되었습니다:

  • Java SE 5에서 제네릭이 추가되었습니다.;
  • 람다 표현식이 도입된 Java 8에서 인터페이스의 기본 메서드와 함께 사용되었습니다.

이 두 가지가 지금까지 이루어진 컬렉션 프레임워크의 가장 중요한 업데이트입니다. 하지만 사실 거의 모든 버전의 JDK에는 컬렉션 프레임워크에 대한 일련의 변경 사항이 있습니다.

이 파트에서는 컬렉션 프레임워크가 제공하는 가장 유용한 데이터 구조와 애플리케이션에서 이 데이터를 조작하는 데 사용할 패턴을 배우게 됩니다.

가장 먼저 알아야 할 것은 기술적 관점에서 볼 때 컬렉션 프레임워크는 다양한 타입의 컨테이너에 데이터를 저장하는 다양한 방법을 모델링하는 인터페이스 집합이라는 점입니다. 그런 다음 프레임워크는 각 인터페이스에 대해 하나 이상의 구현을 제공합니다. 이러한 구현을 아는 것은 인터페이스만큼이나 중요하며, 올바른 구현을 선택하는 것은 필요한 작업에 따라 달라집니다.

 

컬렉션 프레임워크에서 나만의 방법 찾기

컬렉션 프레임워크의 인터페이스와 클래스의 양이 처음에는 압도적으로 느껴질 수 있습니다. 실제로 클래스와 인터페이스 모두 많은 구조체를 사용할 수 있습니다. 일부는 LinkedList처럼 설명이 필요 없는 이름을 가지고 있고, 일부는 ConcurrentHashMap처럼 동작을 수행하며, 일부는 ConcurrentSkipListMap처럼 이상하게 들릴 수도 있습니다.

이러한 요소 중 일부는 다른 요소보다 훨씬 더 자주 사용하게 될 것입니다. Java 언어에 이미 익숙하다면 List, ArrayList, Map을 이미 접해 보셨을 것입니다. 이 튜토리얼은 컬렉션 프레임워크에서 가장 널리 사용되는 구조, 자바 개발자로서 매일 사용하게 될 구조, 그리고 가장 잘 알고 이해해야 하는 구조에 초점을 맞추고 있습니다.

즉, 컬렉션 프레임워크에 어떤 기능이 있는지 큰 그림을 그려야 합니다.

첫째, 프레임워크는 인터페이스와 구현으로 구성됩니다. 올바른 인터페이스를 선택하려면 애플리케이션에 어떤 기능을 제공하려는지 파악해야 합니다. 필요한 기능은 다음과 같이 구성됩니다:

  • 객체를 저장하고 그 위에 반복 작업을 수행하시나요?
  • 오브젝트를 대기열에 밀어넣고 팝하는 방법을 알고 계신가요?
  • 키를 사용하여 검색할 수 있나요?
  • 인덱스로 액세스할 수 있나요?
  • 정렬하고 있나요?
  • 중복 또는 널 값의 존재를 방지할 수 있나요?

올바른 구현을 선택하려면 이러한 기능을 어떻게 사용할지 알아야 합니다:

  • 오브젝트에 액세스하는 방식이 반복 액세스인가요, 아니면 무작위 인덱싱 액세스인가요?
  • 애플리케이션을 시작할 때 객체가 고정되어 있고 수명 기간 동안 크게 변경되지 않나요?
  • 특정 객체의 존재 여부를 많이 확인해야 하는 경우 객체의 양이 중요할까요?
  • 오브젝트를 저장해야 하는 구조가 동시에 액세스되는 구조인가요?

컬렉션 프레임워크는 이러한 모든 문제에 대한 올바른 솔루션을 제공할 수 있습니다.

컬렉션 프레임워크에는 컬렉션과 맵이라는 두 가지 주요 인터페이스 카테고리가 있습니다.

컬렉션은 객체를 저장하고 그 객체를 반복하는 것입니다. Collection 인터페이스는 이 범주의 루트 인터페이스입니다. 사실 Collection 인터페이스는 Iterable 인터페이스를 확장한 것이지만 이 인터페이스는 컬렉션 프레임워크의 일부가 아닙니다.

이 개념에 익숙하다면 데이터베이스에서 기본 키가 객체를 나타내는 것처럼 맵은 객체를 나타내는 키와 함께 객체를 저장합니다. 가끔 맵이 키/값 쌍을 저장한다는 말을 듣게 되는데, 이는 맵이 하는 일을 정확히 설명합니다. Map 인터페이스가 이 범주의 루트 인터페이스입니다.

Collection 계층과 ‘Map’ 계층의 인터페이스 사이에는 직접적인 관계가 없습니다.

이러한 컬렉션과 맵과 함께 Collection 계층 구조에서 큐와 스택을 모델링하는 인터페이스를 찾을 수 있다는 사실도 알아야 합니다. 큐와 스택은 실제로 객체 컬렉션을 반복하는 것은 아니지만, Collection 계층 구조에 추가되었으므로 이를 통해 반복 작업을 수행할 수 있습니다.

마지막으로 알아야 할 계층 구조가 하나 더 있는데, 바로 Iterator 계층 구조입니다. 이터레이터는 객체 컬렉션을 반복할 수 있는 객체로, 컬렉션 프레임워크의 일부입니다.

이를 통해 두 개의 메인 카테고리인 CollectionMap, 하위 카테고리인 Queue, 사이드 카테고리인 Iterator가 만들어집니다.

 

오래된 인터페이스 및 구현 사용 피하기

컬렉션 프레임워크는 Java 2에서 처음 소개되었으며, 이는 그 이전에도 존재했다는 것을 의미합니다. 이 프레임워크는 이전 버전과의 호환성을 유지하기 위해 JDK에 여전히 남아 있지만 애플리케이션에서 더 이상 사용해서는 안 되는 여러 클래스와 인터페이스로 구성되었습니다.

이러한 클래스와 인터페이스는 다음과 같습니다:

  • VectorStack. Vector 클래스는 List 인터페이스를 구현하도록 개조되었습니다. 비동시 환경에서 벡터를 사용하는 경우 ArrayList로 안전하게 대체할 수 있습니다. Stack 클래스는 Vector를 확장하며 비동시 환경에서는 ArrayDeque로 대체해야 합니다.
  • Vector 클래스는 Enumeration 인터페이스를 사용하여 이터레이터를 모델링합니다. 이 인터페이스는 더 이상 사용되어서는 안 됩니다. 이제 선호되는 인터페이스는 Iterator 인터페이스입니다.
  • HashTable: 이 클래스는 Map 인터페이스를 구현하도록 개조되었습니다. 비동시 환경에서 이 클래스의 인스턴스를 사용하는 경우, HashMap으로 안전하게 대체할 수 있습니다. 동시 환경에서는 ConcurrentHashMap을 대체로 사용할 수 있습니다.

 

배열 대신 컬렉션을 선택하는 이유는 무엇인가요?

데이터를 기존의 배열에 넣는 것만으로도 충분한데 굳이 수집 프레임워크를 배워야 하는지 의문이 들 수도 있습니다.

어쨌든 간단하고, 잘 익히고, 필요에 맞는 솔루션이 있다면 반드시 그 솔루션을 고수해야 한다는 사실입니다!

배열이 할 수 없는 컬렉션의 장점은 무엇일까요?

  • 컬렉션은 포함된 요소의 수를 추적합니다.
  • 컬렉션의 용량에는 제한이 없습니다. 컬렉션에 (거의) 원하는 만큼 요소를 추가할 수 있습니다.
  • 컬렉션은 컬렉션에 저장할 수 있는 요소를 제어할 수 있습니다. 예를 들어, null 요소가 추가되지 않도록 할 수 있습니다.
  • 컬렉션에서 지정된 요소의 존재 여부를 쿼리할 수 있습니다.
  • 컬렉션은 다른 컬렉션과 교차하거나 병합하는 등의 작업을 제공합니다.

이것은 컬렉션으로 할 수 있는 일의 작은 샘플에 불과합니다. 실제로 컬렉션은 객체이고 객체는 확장이 가능하기 때문에 JDK에서 제공하는 대부분의 컬렉션에 필요한 모든 연산을 추가할 수 있습니다. 배열은 Java에서 객체가 아니기 때문에 배열에서는 이 작업을 수행할 수 없습니다.