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

[2026] 프로그래밍 언어별 자료구조 비교 | 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

다음은 cpp를 활용한 상세한 구현 코드입니다. 필요한 모듈을 import하고, 반복문으로 데이터를 처리합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

#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

다음은 python를 활용한 상세한 구현 코드입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

# 타입 자유
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

다음은 java를 활용한 상세한 구현 코드입니다. 필요한 모듈을 import하고, 함수를 통해 로직을 구현합니다, 반복문으로 데이터를 처리합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

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

다음은 javascript를 활용한 상세한 구현 코드입니다. 반복문으로 데이터를 처리합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

// 타입 자유, 희소 배열 가능
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
  • 편의성: 다양한 내장 메서드
  • 성능 예측 어려움: 엔진 최적화에 의존

배열 비교 다이어그램

다음은 mermaid를 활용한 상세한 구현 코드입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

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

다음은 cpp를 활용한 상세한 구현 코드입니다. 필요한 모듈을 import하고, 반복문으로 데이터를 처리합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

#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

다음은 python를 활용한 상세한 구현 코드입니다. 필요한 모듈을 import하고. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

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 연결 리스트 성능 비교

아래 코드는 mermaid를 사용한 구현 예제입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

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: 단순 키-값 조회만 필요하고 성능이 중요할 때 시간복잡도: | 연산 | map | unordered_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

다음은 cpp를 활용한 상세한 구현 코드입니다. 필요한 모듈을 import하고, 반복문으로 데이터를 처리합니다, 조건문으로 분기 처리를 수행합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

#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

다음은 python를 활용한 상세한 구현 코드입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

# 순서 미보장
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

다음은 java를 활용한 상세한 구현 코드입니다. 필요한 모듈을 import하고, 함수를 통해 로직을 구현합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

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

다음은 javascript를 활용한 상세한 구현 코드입니다. 반복문으로 데이터를 처리합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

// 삽입 순서 보장
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가 더 나은 선택일 수 있습니다.

메모리 사용량 비교

아래 코드는 mermaid를 사용한 구현 예제입니다. 코드를 직접 실행해보면서 동작을 확인해보세요.

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. 실무 선택 가이드

언어 선택 플로우차트

아래 코드는 mermaid를 사용한 구현 예제입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

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를 사용한 구현 예제입니다. 필요한 모듈을 import하고, 함수를 통해 로직을 구현합니다. 코드를 직접 실행해보면서 동작을 확인해보세요.

# 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프론트엔드 필수, 백엔드도 가능

다음 단계

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

... 996 lines not shown ... Token usage: 63706/1000000; 936294 remaining Start-Sleep -Seconds 3