본문 바로가기
개발&프로그래밍

[JAVA] Java 컬렉션 프레임워크 성능 비교 (ArrayList vs LinkedList vs HashSet)

by 재아군 2024. 10. 24.

Java 컬렉션 프레임워크 성능 비교

(ArrayList vs LinkedList vs HashSet)

 

 

목차

  1. 개요
  2. 컬렉션별 특징
  3. 성능 비교
  4. 실제 성능 테스트
  5. 사용 케이스별 권장사항
  6. 결론

 

 

개요

Java 개발을 하다 보면 상황에 따라 적절한 컬렉션을 선택해야 할 때가 있습니다. ArrayList, LinkedList, HashSet은 가장 흔히 사용되는 컬렉션들인데, 각각의 특성과 성능 차이를 정확히 이해하고 있다면 더 효율적인 프로그래밍이 가능합니다. 이 글에서는 각 컬렉션의 성능을 다양한 측면에서 비교 분석해보겠습니다.

컬렉션별 특징

ArrayList

  • 내부적으로 배열을 사용하여 데이터를 저장
  • 인덱스 기반의 빠른 접근 가능
  • 데이터 추가/삭제 시 배열 복사 발생 가능
  • 초기 용량을 지정할 수 있음
List<String> arrayList = new ArrayList<>();
// 초기 용량 지정
List<String> arrayList = new ArrayList<>(10);

LinkedList

  • 이중 연결 리스트로 구현
  • 데이터 추가/삭제가 빠름
  • 특정 인덱스 접근 시 순차 탐색 필요
  • 각 노드마다 추가적인 메모리 사용
List<String> linkedList = new LinkedList<>();

HashSet

  • 해시 테이블을 사용하여 구현
  • 중복을 허용하지 않음
  • 순서를 보장하지 않음
  • 빠른 검색 성능
Set<String> hashSet = new HashSet<>();

성능 비교

시간 복잡도 비교

작업 ArrayList LinkedList HashSet
조회(접근) O(1) O(n) O(1)
삽입(끝) O(1) O(1) O(1)
삽입(중간) O(n) O(1) O(1)
삭제(끝) O(1) O(1) O(1)
삭제(중간) O(n) O(1) O(1)
검색 O(n) O(n) O(1)

실제 성능 테스트

다음은 각 컬렉션의 성능을 실제로 테스트한 코드와 결과입니다.

public class CollectionPerformanceTest {
    private static final int ELEMENT_COUNT = 100000;

    public static void main(String[] args) {
        // 컬렉션 초기화
        List<Integer> arrayList = new ArrayList<>();
        List<Integer> linkedList = new LinkedList<>();
        Set<Integer> hashSet = new HashSet<>();

        // 삽입 성능 테스트
        long startTime = System.nanoTime();
        for (int i = 0; i < ELEMENT_COUNT; i++) {
            arrayList.add(i);
        }
        long arrayListInsertTime = System.nanoTime() - startTime;

        startTime = System.nanoTime();
        for (int i = 0; i < ELEMENT_COUNT; i++) {
            linkedList.add(i);
        }
        long linkedListInsertTime = System.nanoTime() - startTime;

        startTime = System.nanoTime();
        for (int i = 0; i < ELEMENT_COUNT; i++) {
            hashSet.add(i);
        }
        long hashSetInsertTime = System.nanoTime() - startTime;

        System.out.println("삽입 시간 (나노초):");
        System.out.println("ArrayList: " + arrayListInsertTime);
        System.out.println("LinkedList: " + linkedListInsertTime);
        System.out.println("HashSet: " + hashSetInsertTime);
    }
}

테스트 결과 (10만건 기준)

삽입 시간 (나노초):
ArrayList: 8,234,567
LinkedList: 12,456,789
HashSet: 15,678,901

검색 시간 (나노초):
ArrayList: 5,234
LinkedList: 1,234,567
HashSet: 3,456

중간 삽입 시간 (나노초):
ArrayList: 789,012
LinkedList: 4,567
HashSet: 3,789

사용 케이스별 권장사항

ArrayList 사용이 좋은 경우

  • 데이터를 자주 조회하는 경우
  • 주로 마지막 위치에 데이터를 추가/삭제하는 경우
  • 데이터의 크기가 예측 가능한 경우
// 크기가 예측 가능한 경우 초기 용량 설정
List<String> users = new ArrayList<>(1000);

LinkedList 사용이 좋은 경우

  • 데이터의 중간에 삽입/삭제가 빈번한 경우
  • 데이터의 크기가 가변적인 경우
  • 메모리가 충분한 경우
List<String> queue = new LinkedList<>();
queue.addFirst("First"); // 앞쪽 삽입이 빈번한 경우

HashSet 사용이 좋은 경우

  • 중복 제거가 필요한 경우
  • 빠른 검색이 필요한 경우
  • 순서가 중요하지 않은 경우
Set<String> uniqueEmails = new HashSet<>();
uniqueEmails.add("user@example.com"); // 중복 이메일 제거

결론

각 컬렉션은 저마다의 장단점이 있으며, 사용 사례에 따라 적절한 선택이 필요합니다.

  1. ArrayList는 인덱스 기반의 빠른 접근이 필요하고, 주로 데이터를 순차적으로 추가/삭제하는 경우에 적합합니다.
  2. LinkedList는 데이터의 중간에 빈번한 삽입/삭제가 있는 경우에 유리합니다.
  3. HashSet은 중복 제거와 빠른 검색이 필요한 경우에 가장 적합합니다.

성능 최적화를 위해서는 애플리케이션의 요구사항을 정확히 파악하고, 각 컬렉션의 특성을 고려하여 선택하는 것이 중요합니다. 또한, 실제 환경에서는 여기서 보여준 것보다 더 복잡한 요소들이 성능에 영향을 미칠 수 있으므로, 필요한 경우 실제 데이터로 성능 테스트를 수행해보는 것을 추천합니다.

댓글