[자료구조/Java] Collection (List, ArrayList, LinkedList, Set, Hashset, TreeSet)

현굥·2024년 8월 12일

자료구조

목록 보기
1/3

JCF(Java Collections Framework)

JCF(Java Collections Framework)는 다양한 자료구조와 이들을 구현하는 클래스를 정의하는 인터페이스를 제공합니다.
즉, 컬렉션 프레임워크는 다수의 데이터들을 쉽게 다루기 위해 만들어진 클래스들의 집합체입니다. 클래스들은 인터페이스의 형태로 만들어져있고, 사용시 클래스로 구현해야합니다.

JCF의 주요 인터페이스는 크게 Collection과 Map 두 가지로 분류할 수 있습니다.

  • Collection 인터페이스: 데이터를 순서나 집합 형태로 저장하는 인터페이스입니다.
  • Map 인터페이스: 데이터를 Key-Value 형태로 저장하는 인터페이스입니다.

Collection

Collection은 객체들의 그룹이나 집합을 나타내는 인터페이스로, 여러 객체를 목적에 맞게 그룹화하는 역할을 합니다. 이 인터페이스는 다양한 자료구조에 따라 데이터를 효율적으로 관리할 수 있도록 설계되었으며, 크기가 자유롭게 조정 가능한 컬렉션 클래스를 제공합니다. 또한, 제네릭(Generic)을 사용하여 컬렉션에 저장할 객체의 타입을 명시할 수 있습니다.

Collection의 주요 특징

  • 가변 크기: Collection은 여러 객체를 동적으로 저장하고 관리할 수 있습니다.
  • 객체들의 컨테이너: Collection은 여러 객체를 담는 컨테이너 역할을 합니다.
  • 제네릭 기반: 제네릭을 통해 저장할 객체의 타입을 명확히 지정할 수 있습니다.
  • 데이터의 집합: Collection은 데이터의 집합이나 그룹을 의미하며, 다양한 자료구조로 표현됩니다.

collection 인터페이스는 List, Set, Queue로 크게 세가지 하위 인터페이스로 분류할 수 있습니다.

다음은 Java 컬렉션 프레임워크의 상속구조를 나타냅니다.

이제 Collection의 하위 인터페이스와 클래스에 대해 알아봅시다.
List Collection 를 알아보기에 앞서, 자료구조 관점에서 List에 대해 알아봅시다.


자료구조 관점에서 List

List는 선형 자료구조로, 요소들이 순서대로 배열된 형태로 저장되는 구조를 말합니다. 리스트는 다양한 형태로 구현할 수 있으며, 특정 상황에 따라 효율성이 달라집니다.

배열 기반 리스트_ Array-based list

배열은 List의 일종으로 간주될 수 있습니다. 배열 크기의 변동 가능 여부에 따라 정적배열과 동적배열로 나눌 수 있습니다.

1. 정적 배열 (Static Array)

  • 크기가 고정되며, 선언 시 결정된 크기는 변경할 수 없습니다.
  • 인덱스 기반으로 접근할 수 있어 빠른 검색이 가능합니다.
  • 기본 자료형과 클래스 자료형을 모두 담을 수 있습니다.

2. 동적 배열 (Dynamic Array)

  • ArrayList와 같은 구조로, 필요에 따라 크기가 자동으로 조정됩니다.
  • 배열의 크기가 가득차면, 내부적으로 더 큰 배열을 할당하고 기존 데이터를 복사하여 그 크기를 확장합니다.

연결리스트_ Linked List

연결리스트는 요소들이 노드로 구성되며, 각 노드는 다음 노드를 가리키는 포인터를 포함합니다. 연결 방식에 따라 Singly Linked List와 Doubly Linked List으로 나눌 수 있습니다.

  • 단일 연결 리스트 (Singly Linked List): 각 노드는 다음 노드에 대한 참조를 가집니다.
  • 이중 연결 리스트 (Doubly Linked List): 각 노드는 이전 및 다음 노드에 대한 참조를 가집니다.
  • 동적으로 크기를 할당할 수 있습니다.
  • 중간 값을 빼내면 앞으로 당겨지므로 메모리관점에서 효율적입니다.
  • 클래스 자료형(참조 자료형)만 담을 수 있습니다. (기본 자료형은 담을 수 없음)
  • 타입 안정성을 보장하는 generic을 사용할 수 있다.

이제 자바의 Collection 인터페이스의 하위 인터페이스인 List와 Set에 대해 알아봅시다.


List (Interface)

요소들에 대한 순서를 구분하고, 중복을 허용합니다.

인덱스를 통해 요소를 관리하며, List 컬렉션에 객체를 추가하면 자동으로 인덱스가 부여됩니다.

List컬렉션 인터페이스에는 추가(add), 검색(contain, get, size), 삭제(remove, clear) 등의 메서드가 선언되어있습니다. 그러므로, List컬렉션 인터페이스를 상속해서 구현하는 클래스는 해당 기능을 오버라이드 해야합니다.

이제 list 를 구현한 클래스와 특징들에 대해 알아봅시다.

  • ArrayList : 클래스 내부의 배열을 이용해 데이터를 저장한다. (요소 주소값 연속o)
  • LinkedList : 하나의 데이터가 다음 데이터의 주소값을 포함하는 형식으로 저장한다. (요소 주소값 연속x)

ArrayList 클래스

ArrayList는 List 컬렉션 인터페이스를 구현한 클래스로, 동적 배열로 구성된 List입니다. 인덱스 기반으로 요소를 관리하며, 삽입과 삭제 연산이 빈번하지 않은 경우 적합합니다.

  • 동적 배열: 내부적으로 데이터를 배열에서 관리하며, 크기가 자동으로 조정됩니다.
  • 검색 속도: 인덱스를 통한 빠른 검색이 가능하지만, 삽입/삭제 연산은 느립니다.

ArrayList code

// ArrayList 객체 생성
List<String> list = new ArrayList<>();

// 객체 추가
list.add("Hello");
list.add("gglee");

// 객체 총 개수
int size = list.size();

// 동일한 객체 있는지 검색
boolean isFindValue = list.contains("gglee");

// 인덱스에 위치하는 객체 값 얻기
String str = list.get(0);

// 인덱스를 이용하여 객체 삭제
list.remove(0);

LinkedList

LinkedList는 List컬렉션 인터페이스를 구현한 구현클래스로, doubly linked list 구조를 사용하여 각 요소를 노드로 연결합니다. 각 노드는 다음노드와 이전노드의 주소를 가지고 있어서, 양방향으로 탐색이 가능합니다.

양방향으로 가르키기 때문에 참조하려는 원소에 따라 처음부터 정방향 또는 역순으로 조회할 수 있습니다.
특정 인덱스의 객체를 제거하게 되면, 제거하는 인덱스의 앞뒤 참조만 변경되고 나머지 링크는 변경되지 않습니다.

  • 동적 크기 조정: 크기가 동적으로 조정됩니다.
  • 이중 연결 리스트: 각 요소가 양방향으로 연결되어 있어 효율적인 삽입/삭제가 가능합니다.
  • 탐색 속도: 양방향 탐색이 가능하여 특정 인덱스에서의 작업이 효율적입니다.
import java.util.LinkedList;

public class LinkedListExample {
    public static void main(String[] args) {
        // LinkedList 생성
        LinkedList<String> linkedList = new LinkedList<>();

        // 요소 추가
        linkedList.add("Apple");
        linkedList.add("Banana");
        linkedList.add("Orange");
        linkedList.add("Mango");

        // 첫 번째 위치에 요소 추가
        linkedList.addFirst("Strawberry");

        // 마지막 위치에 요소 추가
        linkedList.addLast("Pineapple");

        // 요소 출력
        System.out.println("LinkedList Elements:");
        for (String item : linkedList) {
            System.out.println(item);
        }

        // 첫 번째와 마지막 요소 확인
        System.out.println("First Element: " + linkedList.getFirst());
        System.out.println("Last Element: " + linkedList.getLast());

        // 요소 제거
        linkedList.remove("Banana");
        System.out.println("After removing 'Banana': " + linkedList);

        // 첫 번째 요소 제거
        linkedList.removeFirst();
        System.out.println("After removing the first element: " + linkedList);

        // 마지막 요소 제거
        linkedList.removeLast();
        System.out.println("After removing the last element: " + linkedList);
    }
}

Set (Interface)

Set는 HashSet과 TreeSet이 상속받고 있습니다. List와 함께 Collection 을 상속받고 있어 둘의 사용하는 메소드가 비슷합니다.

그러나, Set은 저장된 값이 순서가 없으며, 중복을 허용하지 않습니다.

Set 을 구현한 클래스들은 아래와 같은 특징을 가집니다.

데이터의 중복을 허용하지 않는다.
데이터의 순서가 없다. (인덱스x)


HashSet

  • Set에서 가장 많이 사용하는 클래스
  • 해시 알고리즘(hash algorithm)을 사용해서 검색속도가 빠릅니다.
  • 중복을 허용하지 않습니다.

데이터를 저장하는데 순서가 없는 이유는 해시때문이고, 데이터를 배열에 저장하긴 하는데 연속된 주소를 통해 인덱스를 나누지 않습니다. 즉, 저장할 데이터의 키값을 해시함수에 넣고, 반환값으로 배열의 인덱스를 구합니다. 그러고나서, 해당 인덱스에 저장된 연결리스트에 데이터를 넣어 저장하게 됩니다.
그렇기 때문에, 데이터를 추가하거나 삭제할 때 데이터의 이동이 없기때문에 속도 측면에서 효율적입니다.


 import java.util.HashSet;

public class HashSetExample {
   public static void main(String[] args) {
       // HashSet 생성
       HashSet<String> hashSet = new HashSet<>();

       // 요소 추가
       hashSet.add("Apple");
       hashSet.add("Banana");
       hashSet.add("Orange");
       hashSet.add("Mango");

       // 중복된 요소 추가 (추가되지 않음)
       hashSet.add("Apple");

       // 요소 출력
       System.out.println("HashSet Elements:");
       for (String item : hashSet) {
           System.out.println(item);
       }

       // 요소 존재 여부 확인
       boolean containsApple = hashSet.contains("Apple");
       System.out.println("Contains 'Apple'? " + containsApple);

       // 요소 제거
       hashSet.remove("Banana");
       System.out.println("After removing 'Banana': " + hashSet);
   }
}

TreeSet

값을 정렬하지만 정렬방법을 지정할 수는 없다.
그래서 HashSet보다 상대적으로 느립니다.


import java.util.TreeSet;

public class TreeSetExample {
  public static void main(String[] args) {
      // TreeSet 생성
      TreeSet<String> treeSet = new TreeSet<>();

      // 요소 추가 (자동으로 정렬됨)
      treeSet.add("Apple");
      treeSet.add("Banana");
      treeSet.add("Orange");
      treeSet.add("Mango");

      // 요소 출력 (정렬된 순서로 출력)
      System.out.println("TreeSet Elements:");
      for (String item : treeSet) {
          System.out.println(item);
      }

      // 첫 번째와 마지막 요소 확인
      System.out.println("First Element: " + treeSet.first());
      System.out.println("Last Element: " + treeSet.last());

      // 요소 제거
      treeSet.remove("Orange");
      System.out.println("After removing 'Orange': " + treeSet);
  }
}

0개의 댓글