Java Collection Framework - 종류와 Iterator Interface의 개념

안녕하세요.

 

Java Collection Framework에 대해 알아보고 합니다. 

 

먼저, Java Collection Framework란 무엇일까요?

 

어떤 장점이 있어 사용하고 어떻게 구성되어 있을까요.

 

 

JCF라고도 불리는 Java Collection Framework는 Java로 작성된 Data Structure의 모음집입니다. 

사용자들이 많이 사용할 Data Structure를 사전에 정의해둔 것인데요.

 

만약, 여러분들이 C언어를 사용하며 LinkedList를 자료 구조를 사용하고 싶다면 어떻게 해야 할까요? 

 

아래와 같이 구조체를 형성하고 자료 구조를 직접 구현해야 할 것입니다.

#include <stdio.h>
#include <stdlib.h>

// 노드 구조체 정의
typedef struct Node {
    int data;
    struct Node* next;
} Node;

// 리스트에 새로운 노드 추가
void append(Node** head_ref, int new_data) {
    Node* new_node = (Node*)malloc(sizeof(Node));
    Node* last = *head_ref;
    
    new_node->data = new_data;
    new_node->next = NULL;
    
    if (*head_ref == NULL) {
        *head_ref = new_node;
        return;
    }
    
    while (last->next != NULL) {
        last = last->next;
    }
    
    last->next = new_node;
}

// 리스트 출력
void printList(Node* node) {
    while (node != NULL) {
        printf("%d -> ", node->data);
        node = node->next;
    }
    printf("NULL\n");
}

// 첫 번째 노드 제거
void removeFirst(Node** head_ref) {
    if (*head_ref == NULL) return;
    Node* temp = *head_ref;
    *head_ref = (*head_ref)->next;
    free(temp);
}

 

하지만, 자바의 Collection Framework를 사용하게 된다면 아래와 같이 한 줄로 LinkedList를 사용할 수 있습니다. 

 

또한, JVM에 맞춰 최적화, 경량화가 이루어져 있기 때문에 안정성과 효율성 측면에서 장점을 가지고 있습니다. 

import java.util.LinkedList;

public class Main {
    public static void main(String[] args) {
        // LinkedList 객체 생성
        LinkedList<String> list = new LinkedList<>();

 

그럼 Java Collection Framework을 사용하게 된다면 어떤 장점이 존재할까요??? 

 

아래와 같이 인터페이스와 다형성을 이용한 객체지향적 설계를 통해 재사용성이 높으며 사용법을 익히기에 편리하다는 장점이 존재합니다. 

또한, 상위 인터페이스 타입으로 업캐스팅이 가능하며 JVM의 최적화되어있으며 학습 시간을 줄일 수 있다는 장점이 존재합니다. 

 

 

Java Collection Framework에는 어떤 종류가 존재할까요??? 

하나하나 알아보도록 하겠습니다. 

 

 

Java Collection Framework는 크게 2가지 interface로 구분됩니다.

 

Collectoin Interface와 Map Interface로 구분됩니다.

 

여러분들이 많이 사용하고 계시는 List, Set Interface의 경우 공통부분이 다수 존재하여 Collection Interface를 상속받아 사용하고 있고 있는데요. 

 

또한, Map Interface의 경우 두 개의 데이터를 한 쌍으로 관리하고 있기 때문에 분리되어있습니다. 

 

 

이러한 구조를 다이어그램으로 확인해 볼까요? 

아래의 이미지는 Collection Interface의 구조도입니다. 

 

그럼 Map Interface의 구조도 확인해 볼까요? 

 

해당 Java Collection Framework를 한눈에 볼 수 있도록 표현하면 아래와 같습니다.

 

지금부터 해당 Java Collection Framework의 모든 자료구조에 대해 알아볼 예정입니다. 

 

이번 포스팅에서는 Iterable에 대해서만 자세히 알아보도록 하겠습니다. 

 

Iterable Interface의 경우 Collection Interface들의 최상단 인터페이스로 Collection 순회 시 Iterator객체를 이용할 때 사용합니다. 

알고리즘 문제를 풀어보시다 보면 순회 시 Iterator를 사용하는 경우를 종종 보신 경우가 있을 거라고 생각합니다. 

다만, Map은 Iterator를 통해 순회가 불가능한데요. 그 이유는 위의 구성도에서 보시는 것과 같이 Iterable Interface를 상속받고 있지 않기 때문입니다. 

 

그렇기 때문에 순회를 위해서는 Stream을 사용하거나 Collection 으로의 변환을 통해 순회해야 합니다.

 

그럼 Iterable Interface의 Iterator를 사용하는 예시 하나를 보고 마무리해 볼까 합니다. 

아래 문제를 풀어보시면 LinkedList로 구현 시 Index 접근을 하게 되면 시간 초과가 발생하는 것을 확인할 수 있고 

Iterator를 사용하게 된다면 통과할 수 있는 것을 확인할 수 있는데요.

https://www.acmicpc.net/problem/1406

 

그럼 왜 Iterator의 순회가 더 빠른 성능을 보여주는 것일까요? 

바로, 내부 구조의 차이 때문인데요. 아래 이미지와 같이 리스트의 경우 원하는 인덱스를 검색하기 위해 앞에서부터 순차적으로 인덱스까지 조회하여 반환하게 되는데요.

즉, 이전의 위치를 기억하지 못해 B를 찾는 경우에도 2번의 조회 C를 조회하는 경우에도 3번의 조회가 발생하는 등 이전에 B를 탐색했다고 해서 추가적으로 한 번의 연산으로 C를 찾는 것이 아닌 다시 처음부터 A, B, C 순으로 조회하게 됩니다. 

 

그로 인해 List의 길이가 길어짐에 따라 최악의 경우에 가까워지며 최대 O(N)의 시간이 소요됨을 확인할 수 있습니다.

 

반면, Iterator의 경우 현재 위치를 기억하기 때문에 순차적으로 접근하는 상황이 존재한다면 보다 유리할 수 있는데요.

A를 찾는데 1번의 연산, 그 후 B를 찾는데 1번의 연산이 발생함으로 처음부터 조회하는 것이 아닌 현재 위치로부터 조회가 가능하다는 장점이 존재하고 시간복잡도는 O(1)의 복잡도를 가지게 되어 보다 빠른 연산이 가능해집니다. 

 

 

 

 

오늘은 Java Collection Framework의 구성 및 종류를 알아보고 Iterable Interface에 대해 알아보았는데요.

 

다음 포스팅에서는 Iterable Interface를 상속받고 있는 Collection Interface를 학습해 보도록 하겠습니다.