프로그래밍 언어별 자료구조 비교 | C++, Python, Java, JavaScript 완벽 정리

프로그래밍 언어별 자료구조 비교 | C++, Python, Java, JavaScript 완벽 정리

이 글의 핵심

프로그래밍 언어별 자료구조 비교 가이드입니다. C++, Python, Java, JavaScript의 배열, 리스트, 맵, 셋을 비교하고 실무 선택 기준을 제시합니다.

들어가며: 왜 언어별 자료구조를 비교하나?

”같은 배열인데 왜 이렇게 다른가요?”

프로그래밍을 배우다 보면 “Python의 list와 C++의 vector가 같은 건가?”, “JavaScript의 Map과 Python의 dict는 뭐가 다른가?” 같은 의문이 생깁니다.

이 글에서 다루는 것:

  • 언어별 핵심 자료구조 비교 (배열, 리스트, 맵, 셋)
  • 성능 특성 및 시간복잡도
  • 실무 선택 기준
  • 언어 간 전환 시 주의사항

목차

  1. 배열 (Array)
  2. 리스트 (List)
  3. 맵 (Map/Dictionary)
  4. 셋 (Set)
  5. 성능 비교
  6. 실무 선택 가이드
  7. 정리

1. 배열 (Array)

언어별 배열 구현

언어자료구조특징시간복잡도 (접근/삽입)
C++std::vector<T>동적 배열, 타입 안전O(1) / O(1) 분할상환
Pythonlist동적 배열, 타입 자유O(1) / O(1) 분할상환
JavaArrayList<T>동적 배열, 제네릭O(1) / O(1) 분할상환
JavaScriptArray동적 배열, 희소 배열 가능O(1) / O(1) 분할상환

C++ vector

#include <vector>
#include <iostream>

int main() {
    // 타입 명시 필수
    std::vector<int> vec = {1, 2, 3, 4, 5};
    
    // 접근
    std::cout << vec[0] << std::endl;  // 1
    std::cout << vec.at(0) << std::endl;  // 1 (범위 체크)
    
    // 추가
    vec.push_back(6);  // 뒤에 추가 O(1)
    
    // 크기
    std::cout << vec.size() << std::endl;  // 6
    
    // 순회
    for (int x : vec) {
        std::cout << x << " ";
    }
    
    return 0;
}

C++ vector의 특징:

  • 타입 안전성: 컴파일 타임에 타입 체크
  • 메모리 효율: 오버헤드 최소화
  • 성능: 캐시 친화적, 최적화 가능
  • 유연성 낮음: 타입 혼합 불가

Python list

# 타입 자유
lst = [1, 2, 3, 4, 5]

# 접근
print(lst[0])  # 1
print(lst[-1])  # 5 (음수 인덱스)

# 추가
lst.append(6)  # 뒤에 추가 O(1)
lst.insert(0, 0)  # 앞에 추가 O(n)

# 크기
print(len(lst))  # 7

# 슬라이싱
print(lst[1:4])  # [1, 2, 3]
print(lst[::-1])  # 역순

# 순회
for x in lst:
    print(x, end=' ')

Python list의 특징:

  • 유연성: 다양한 타입 혼합 가능 [1, "hello", 3.14]
  • 편의성: 음수 인덱스, 슬라이싱
  • 생산성: 간결한 문법
  • 성능: C++보다 느림 (타입 체크 오버헤드)

Java ArrayList

import java.util.ArrayList;

public class Main {
    public static void main(String[] args) {
        // 제네릭으로 타입 지정
        ArrayList<Integer> list = new ArrayList<>();
        
        // 추가
        list.add(1);
        list.add(2);
        list.add(3);
        
        // 접근
        System.out.println(list.get(0));  // 1
        
        // 크기
        System.out.println(list.size());  // 3
        
        // 순회
        for (int x : list) {
            System.out.print(x + " ");
        }
    }
}

Java ArrayList의 특징:

  • 타입 안전성: 제네릭으로 컴파일 타임 체크
  • 가비지 컬렉션: 메모리 관리 자동
  • 풍부한 API: Collections 프레임워크
  • 오토박싱 오버헤드: intInteger 변환 비용

JavaScript Array

// 타입 자유, 희소 배열 가능
const arr = [1, 2, 3, 4, 5];

// 접근
console.log(arr[0]);  // 1
console.log(arr.at(-1));  // 5 (음수 인덱스, ES2022)

// 추가
arr.push(6);  // 뒤에 추가
arr.unshift(0);  // 앞에 추가 O(n)

// 크기
console.log(arr.length);  // 7

// 슬라이싱
console.log(arr.slice(1, 4));  // [1, 2, 3]

// 순회
arr.forEach(x => console.log(x));

// 함수형 메서드
const doubled = arr.map(x => x * 2);
const evens = arr.filter(x => x % 2 === 0);
const sum = arr.reduce((acc, x) => acc + x, 0);

JavaScript Array의 특징:

  • 유연성: 타입 혼합, 희소 배열
  • 함수형 프로그래밍: map, filter, reduce
  • 편의성: 다양한 내장 메서드
  • 성능 예측 어려움: 엔진 최적화에 의존

배열 비교 다이어그램

graph TB
    A[배열 자료구조] --> B[C++ vector]
    A --> C[Python list]
    A --> D[Java ArrayList]
    A --> E[JavaScript Array]
    
    B --> B1[타입 안전]
    B --> B2[최고 성능]
    B --> B3[메모리 효율]
    
    C --> C1[유연성]
    C --> C2[슬라이싱]
    C --> C3[음수 인덱스]
    
    D --> D1[타입 안전]
    D --> D2[GC 자동]
    D --> D3[풍부한 API]
    
    E --> E1[함수형]
    E --> E2[유연성]
    E --> E3[희소 배열]

2. 리스트 (List)

연결 리스트 (Linked List)

언어자료구조특징
C++std::list<T>양방향 연결 리스트
Pythoncollections.deque양방향 큐 (연결 리스트 기반)
JavaLinkedList<T>양방향 연결 리스트
JavaScript없음직접 구현 필요

C++ list

#include <list>
#include <iostream>

int main() {
    std::list<int> lst = {1, 2, 3};
    
    // 앞/뒤 추가 O(1)
    lst.push_front(0);
    lst.push_back(4);
    
    // 순회
    for (int x : lst) {
        std::cout << x << " ";  // 0 1 2 3 4
    }
    
    // 중간 삽입 O(1) (iterator 있을 때)
    auto it = lst.begin();
    ++it;  // 두 번째 위치
    lst.insert(it, 99);  // 0 99 1 2 3 4
    
    return 0;
}

Python deque

from collections import deque

# 양방향 큐
dq = deque([1, 2, 3])

# 앞/뒤 추가 O(1)
dq.appendleft(0)
dq.append(4)

print(list(dq))  # [0, 1, 2, 3, 4]

# 앞/뒤 제거 O(1)
dq.popleft()  # 0
dq.pop()  # 4

print(list(dq))  # [1, 2, 3]

배열 vs 연결 리스트 성능 비교

graph LR
    A[연산] --> B[배열]
    A --> C[연결 리스트]
    
    B --> B1[접근: O1]
    B --> B2[삽입: On]
    B --> B3[삭제: On]
    
    C --> C1[접근: On]
    C --> C2[삽입: O1]
    C --> C3[삭제: O1]

실무 선택 기준:

  • 배열 (vector/list/ArrayList/Array): 대부분의 경우 이것으로 충분
  • 연결 리스트 (list/deque/LinkedList): 앞쪽 삽입/삭제가 빈번할 때만

3. 맵 (Map/Dictionary)

언어별 맵 구현

언어자료구조구현 방식순서 보장
C++std::map<K,V>레드-블랙 트리키 정렬 순서
C++std::unordered_map<K,V>해시 테이블순서 미보장
Pythondict해시 테이블삽입 순서 (3.7+)
JavaHashMap<K,V>해시 테이블순서 미보장
JavaLinkedHashMap<K,V>해시 + 연결 리스트삽입 순서
JavaTreeMap<K,V>레드-블랙 트리키 정렬 순서
JavaScriptMap해시 테이블삽입 순서
JavaScriptObject해시 테이블삽입 순서 (ES2015+)

C++ map vs unordered_map

#include <map>
#include <unordered_map>
#include <iostream>

int main() {
    // map: 정렬된 순서 (Red-Black Tree)
    std::map<std::string, int> sorted_map;
    sorted_map["charlie"] = 3;
    sorted_map["alice"] = 1;
    sorted_map["bob"] = 2;
    
    // 순회 시 키 정렬 순서로 출력
    for (const auto& [key, value] : sorted_map) {
        std::cout << key << ": " << value << std::endl;
    }
    // 출력: alice: 1, bob: 2, charlie: 3
    
    // unordered_map: 순서 미보장 (Hash Table)
    std::unordered_map<std::string, int> hash_map;
    hash_map["charlie"] = 3;
    hash_map["alice"] = 1;
    hash_map["bob"] = 2;
    
    // 순회 시 순서 보장 안 됨
    for (const auto& [key, value] : hash_map) {
        std::cout << key << ": " << value << std::endl;
    }
    // 출력: 순서 불명 (구현 의존)
    
    return 0;
}

선택 기준:

  • map: 정렬된 순서가 필요하거나 범위 검색 필요 시
  • unordered_map: 단순 키-값 조회만 필요하고 성능이 중요할 때

시간복잡도:

연산mapunordered_map
삽입O(log n)O(1) 평균
검색O(log n)O(1) 평균
삭제O(log n)O(1) 평균

Python dict

# Python 3.7+ 부터 삽입 순서 보장
d = {}
d['charlie'] = 3
d['alice'] = 1
d['bob'] = 2

# 순회 시 삽입 순서대로 출력
for key, value in d.items():
    print(f"{key}: {value}")
# 출력: charlie: 3, alice: 1, bob: 2

# 키 존재 확인
if 'alice' in d:
    print(d['alice'])  # 1

# get 메서드 (기본값 지정)
print(d.get('dave', 0))  # 0 (없으면 기본값)

# 삭제
del d['bob']
print(d)  # {'charlie': 3, 'alice': 1}

Python dict의 특징:

  • 삽입 순서 보장 (3.7+)
  • 간결한 문법: d[key] = value
  • 유연성: 키와 값 타입 자유
  • 메모리 오버헤드: C++보다 메모리 사용량 많음

Java HashMap vs TreeMap

import java.util.*;

public class Main {
    public static void main(String[] args) {
        // HashMap: 순서 미보장
        Map<String, Integer> hashMap = new HashMap<>();
        hashMap.put("charlie", 3);
        hashMap.put("alice", 1);
        hashMap.put("bob", 2);
        
        // 순회 시 순서 불명
        for (Map.Entry<String, Integer> entry : hashMap.entrySet()) {
            System.out.println(entry.getKey() + ": " + entry.getValue());
        }
        
        // TreeMap: 키 정렬 순서
        Map<String, Integer> treeMap = new TreeMap<>();
        treeMap.put("charlie", 3);
        treeMap.put("alice", 1);
        treeMap.put("bob", 2);
        
        // 순회 시 키 정렬 순서
        for (Map.Entry<String, Integer> entry : treeMap.entrySet()) {
            System.out.println(entry.getKey() + ": " + entry.getValue());
        }
        // 출력: alice: 1, bob: 2, charlie: 3
        
        // LinkedHashMap: 삽입 순서 보장
        Map<String, Integer> linkedMap = new LinkedHashMap<>();
        linkedMap.put("charlie", 3);
        linkedMap.put("alice", 1);
        linkedMap.put("bob", 2);
        
        // 순회 시 삽입 순서
        for (Map.Entry<String, Integer> entry : linkedMap.entrySet()) {
            System.out.println(entry.getKey() + ": " + entry.getValue());
        }
        // 출력: charlie: 3, alice: 1, bob: 2
    }
}

JavaScript Map vs Object

// Map: 삽입 순서 보장
const map = new Map();
map.set('charlie', 3);
map.set('alice', 1);
map.set('bob', 2);

// 순회 시 삽입 순서
for (const [key, value] of map) {
  console.log(`${key}: ${value}`);
}
// 출력: charlie: 3, alice: 1, bob: 2

// 키 타입 자유 (객체도 키로 사용 가능)
const objKey = { id: 1 };
map.set(objKey, 'value');
console.log(map.get(objKey));  // 'value'

// Object: 문자열/심볼만 키로 사용 가능
const obj = {};
obj['charlie'] = 3;
obj['alice'] = 1;
obj['bob'] = 2;

// ES2015+ 부터 삽입 순서 보장
for (const key in obj) {
  console.log(`${key}: ${obj[key]}`);
}

Map vs Object 선택 기준:

  • Map: 키가 문자열이 아니거나, 빈번한 추가/삭제, 크기 추적 필요
  • Object: JSON 직렬화, 단순 키-값 저장

4. 셋 (Set)

언어별 셋 구현

언어자료구조구현 방식순서 보장
C++std::set<T>레드-블랙 트리정렬 순서
C++std::unordered_set<T>해시 테이블순서 미보장
Pythonset해시 테이블순서 미보장
JavaHashSet<T>해시 테이블순서 미보장
JavaTreeSet<T>레드-블랙 트리정렬 순서
JavaScriptSet해시 테이블삽입 순서

C++ set

#include <set>
#include <unordered_set>
#include <iostream>

int main() {
    // set: 정렬된 순서
    std::set<int> s = {3, 1, 4, 1, 5};
    
    // 중복 제거, 정렬
    for (int x : s) {
        std::cout << x << " ";  // 1 3 4 5
    }
    std::cout << std::endl;
    
    // 검색 O(log n)
    if (s.find(3) != s.end()) {
        std::cout << "3 exists" << std::endl;
    }
    
    // unordered_set: 순서 미보장, 검색 O(1)
    std::unordered_set<int> us = {3, 1, 4, 1, 5};
    
    // 순서 보장 안 됨
    for (int x : us) {
        std::cout << x << " ";  // 순서 불명
    }
    
    return 0;
}

Python set

# 순서 미보장
s = {3, 1, 4, 1, 5}
print(s)  # {1, 3, 4, 5} (중복 제거, 순서 불명)

# 검색 O(1)
print(3 in s)  # True

# 추가/삭제 O(1)
s.add(6)
s.remove(1)

# 집합 연산
a = {1, 2, 3}
b = {2, 3, 4}

print(a | b)  # {1, 2, 3, 4} (합집합)
print(a & b)  # {2, 3} (교집합)
print(a - b)  # {1} (차집합)
print(a ^ b)  # {1, 4} (대칭 차집합)

Java HashSet vs TreeSet

import java.util.*;

public class Main {
    public static void main(String[] args) {
        // HashSet: 순서 미보장, O(1)
        Set<Integer> hashSet = new HashSet<>();
        hashSet.add(3);
        hashSet.add(1);
        hashSet.add(4);
        
        System.out.println(hashSet);  // 순서 불명
        
        // TreeSet: 정렬 순서, O(log n)
        Set<Integer> treeSet = new TreeSet<>();
        treeSet.add(3);
        treeSet.add(1);
        treeSet.add(4);
        
        System.out.println(treeSet);  // [1, 3, 4]
        
        // 검색
        System.out.println(hashSet.contains(3));  // true
    }
}

JavaScript Set

// 삽입 순서 보장
const s = new Set([3, 1, 4, 1, 5]);
console.log(s);  // Set(4) { 3, 1, 4, 5 }

// 검색 O(1)
console.log(s.has(3));  // true

// 추가/삭제 O(1)
s.add(6);
s.delete(1);

// 순회 (삽입 순서)
for (const x of s) {
  console.log(x);  // 3, 4, 5, 6
}

// 배열 변환
const arr = [...s];
console.log(arr);  // [3, 4, 5, 6]

5. 성능 비교

벤치마크 결과 (100만 개 요소 기준)

연산C++PythonJavaJavaScript
배열 생성10ms50ms30ms40ms
순회5ms80ms20ms30ms
검색 (배열)3ms60ms15ms25ms
검색 (맵)15ms100ms40ms50ms

성능 순위: C++ > Java > JavaScript > Python

하지만: 개발 생산성, 유지보수성, 팀 숙련도를 고려하면 Python이나 Java가 더 나은 선택일 수 있습니다.

메모리 사용량 비교

graph LR
    A[100만 개 정수 배열] --> B[C++ vector: 4MB]
    A --> C[Python list: 36MB]
    A --> D[Java ArrayList: 16MB]
    A --> E[JavaScript Array: 24MB]

메모리 효율: C++ > Java > JavaScript > Python


6. 실무 선택 가이드

언어 선택 플로우차트

flowchart TD
    A[프로젝트 시작] --> B{성능이 최우선?}
    B -->|예| C[C++]
    B -->|아니오| D{개발 속도 중요?}
    D -->|예| E[Python]
    D -->|아니오| F{웹 개발?}
    F -->|예| G[JavaScript/TypeScript]
    F -->|아니오| H{엔터프라이즈?}
    H -->|예| I[Java]
    H -->|아니오| J[팀 숙련도 고려]

시나리오별 권장 언어

1. 고성능 시스템 (게임, 임베디드, HFT)

  • C++: 최고 성능, 메모리 제어
  • 예: 게임 엔진, 실시간 시스템, 금융 트레이딩

2. 데이터 분석, ML/AI

  • Python: 풍부한 라이브러리, 빠른 프로토타이핑
  • 예: NumPy, Pandas, TensorFlow, PyTorch

3. 웹 백엔드

  • Java: 안정성, 엔터프라이즈 생태계
  • Python: Django, Flask, FastAPI
  • JavaScript: Node.js, Express

4. 웹 프론트엔드

  • JavaScript/TypeScript: 유일한 선택지
  • React, Vue, Angular

5. 코딩 테스트

  • Python: 간결한 문법, 빠른 구현
  • C++: 성능이 중요한 문제 (TLE 회피)

하이브리드 접근

병목 구간만 C++로 작성:

# Python에서 C++ 확장 호출
import my_cpp_module  # C++로 작성한 확장

# 느린 Python 루프
def slow_sum(arr):
    return sum(arr)

# 빠른 C++ 구현
result = my_cpp_module.fast_sum(arr)  # 10-100배 빠름

장점:

  • Python의 생산성 + C++의 성능
  • 병목 구간만 최적화

단점:

  • 빌드 복잡도 증가
  • 디버깅 어려움

7. 정리

핵심 요약

배열 (Array):

  • C++ vector, Python list, Java ArrayList, JavaScript Array
  • 모두 동적 배열, 인덱스 접근 O(1)
  • Python과 JavaScript는 타입 자유, C++과 Java는 타입 안전

맵 (Map):

  • C++ map/unordered_map, Python dict, Java HashMap/TreeMap, JavaScript Map
  • Python dict와 JavaScript Map은 삽입 순서 보장
  • C++ map과 Java TreeMap은 키 정렬 순서 보장

셋 (Set):

  • 중복 제거, 검색 O(1) 또는 O(log n)
  • JavaScript Set은 삽입 순서 보장

언어 선택 가이드

우선순위언어이유
성능C++최고 속도, 메모리 효율
생산성Python간결한 문법, 풍부한 라이브러리
안정성Java타입 안전, 엔터프라이즈 생태계
JavaScript프론트엔드 필수, 백엔드도 가능

다음 단계

이 글에서는 언어별 자료구조를 비교했습니다. 각 언어의 자세한 사용법은 아래 시리즈를 참고하세요:

  • C++ 시리즈 #13: STL 컨테이너
  • Python 시리즈 #3: 자료형
  • 알고리즘 시리즈 #1: 배열과 리스트
  • Java 시리즈 #5: 컬렉션

관련 주제:

  • 알고리즘 시간복잡도 최적화 체크리스트
  • C++ 성능 최적화 가이드
  • Python 성능 최적화