언어별 자료구조 비교: C++, Python, Java, JavaScript 표준 컬렉션

이 글의 핵심

Python의 list와 C++의 vector, JavaScript의 Map과 Python의 dict는 비슷해 보여도 정렬 여부, 해시 기반인지 트리 기반인지, 메모리 배치가 다릅니다. 언어를 옮겨 갈 때 같은 코드가 다른 성능을 내는 이유를 구현 방식에서 찾고, 벤치마크 비교를 통해 실무에서 컬렉션을 고르는 감각을 기르게 합니다.

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

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

이 글에서 다루는 것:

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

배열: vector, list, ArrayList, 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 프레임워크
  • 오토박싱 오버헤드: int → Integer 변환 비용

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
  • 편의성: 다양한 내장 메서드
  • 성능 예측 어려움: 엔진 최적화에 의존

네 컬렉션 모두 “끝에 추가는 분할상환 O(1)“이지만, 용량이 차면 더 큰 버퍼를 새로 잡고 전부 복사한다는 동작은 같습니다. 차이는 늘리는 비율과 그 순간의 부작용입니다. C++ vector는 재할당이 일어나면 기존 원소를 가리키던 포인터·참조·반복자가 모두 무효화되므로, auto& first = vec[0]; vec.push_back(x); 뒤에 first를 쓰면 해제된 메모리를 읽게 됩니다. Python이나 Java에서 넘어온 개발자가 가장 자주 밟는 함정인데, 그 언어들에서는 원소가 별도 객체라 리스트가 커져도 이미 꺼낸 참조가 유효하기 때문입니다. 크기를 미리 안다면 C++의 reserve, Java의 new ArrayList<>(capacity)로 재할당 자체를 줄일 수 있습니다.

인덱스 범위를 벗어났을 때의 동작도 언어마다 다릅니다. Python은 IndexError, Java는 IndexOutOfBoundsException을 던지지만, JavaScript는 undefined를 돌려주고 계속 실행되며, C++의 operator[]는 검사하지 않아 정의되지 않은 동작이 됩니다(검사가 필요하면 at()). JavaScript에서 arr[100] = 1처럼 길이를 크게 넘어 대입하면 에러 대신 구멍 난 희소 배열이 되고, V8은 이런 배열을 느린 딕셔너리 표현으로 바꿔 이후 모든 접근이 느려질 수 있습니다.

배열 비교 다이어그램

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[희소 배열]

연결 리스트와 deque

연결 리스트 (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): 대부분의 경우 이것으로 충분
  • 양쪽 끝 삽입/삭제가 빈번할 때: 연결 리스트보다 C++ std::deque, Java ArrayDeque, Python collections.deque가 먼저입니다
  • 연결 리스트 (std::list/LinkedList): 원소를 가리키는 반복자를 오래 들고 있으면서 중간 삽입·삭제를 해야 하고, 원소 주소가 바뀌면 안 될 때

다이어그램의 “연결 리스트 삽입 O(1)“에는 조건이 붙습니다. 삽입할 위치의 반복자를 이미 갖고 있을 때만 O(1)이고, “i번째 위치에 삽입”이라면 그 위치까지 걸어가는 데 O(n)이 듭니다. 게다가 연결 리스트는 노드가 힙에 흩어져 있어 순회할 때마다 캐시 미스가 나고, 노드마다 포인터 두 개와 할당 오버헤드가 붙습니다. 그래서 중간 삽입이 잦아 보이는 작업도 실제로 재 보면 원소를 통째로 밀어내는 배열이 더 빠른 경우가 많습니다. Java의 LinkedList는 이런 이유로 실무에서 거의 권장되지 않고, Python의 deque도 순수 연결 리스트가 아니라 원소 64개짜리 블록을 연결한 구조라 양 끝 연산은 빠르지만 dq[i] 같은 중간 인덱싱은 O(n)입니다.


맵: 해시 기반과 트리 기반

언어별 맵 구현

언어자료구조구현 방식순서 보장
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 <string>
#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: 단순 키-값 조회만 필요하고 성능이 중요할 때

C++ map의 operator[]는 키가 없으면 기본값으로 새 항목을 삽입합니다. 그래서 if (m["dave"] == 0)처럼 존재 여부를 확인하려던 코드가 맵에 dave: 0을 추가해 버리고, const map에서는 아예 컴파일되지 않습니다. 조회만 할 때는 find나 C++20의 contains를 씁니다. Python dict의 d['dave']가 KeyError를 던지고 d.get('dave', 0)은 삽입하지 않는 것과 반대라, 두 언어를 오가는 사람이 헷갈리기 쉬운 부분입니다.

시간복잡도:

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

해시 테이블의 “O(1) 평균”은 해시가 고르게 퍼진다는 가정 위에 있습니다. 많은 키가 같은 버킷에 몰리면 최악의 경우 O(n)이 되는데, 언어마다 이 최악을 다루는 방식이 다릅니다. Java 8의 HashMap은 한 버킷에 충돌이 8개를 넘으면 그 버킷을 연결 리스트에서 레드-블랙 트리로 바꿔 O(log n)으로 제한합니다. Python과 JavaScript 엔진은 문자열 해시에 프로세스마다 다른 무작위 시드를 섞어, 외부 입력으로 충돌을 일부러 만드는 해시 플러딩 공격을 어렵게 합니다. C++ 표준 라이브러리의 std::hash<int>는 많은 구현에서 값을 그대로 쓰는 항등 함수라, 특정 간격의 정수만 넣으면 충돌이 몰려 느려질 수 있습니다. 외부에서 키를 받는 C++ 서비스라면 해시 함수를 직접 지정하는 것을 고려해야 합니다.

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 직렬화, 단순 키-값 저장

셋: 해시 기반과 트리 기반

언어별 셋 구현

언어자료구조구현 방식순서 보장
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} (대칭 차집합)

위 예제에서 print(s)가 {1, 3, 4, 5}처럼 정렬된 것처럼 보이는 것은 우연에 가깝습니다. CPython에서 작은 정수의 해시값은 그 정수 자신이라 작은 정수 집합은 해시 테이블 슬롯 순서가 크기 순서와 같아지기 쉬울 뿐입니다. 문자열 집합은 실행할 때마다 순서가 바뀌는데, 앞에서 말한 해시 무작위화(PYTHONHASHSEED) 때문입니다. 테스트에서 set이나 문자열 키를 순회한 결과를 그대로 비교하면 로컬에서는 통과하고 CI에서 가끔 실패하는 불안정한 테스트가 되므로, 순서가 필요하면 sorted(s)로 명시적으로 정렬해야 합니다.

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]

성능과 메모리 차이가 생기는 이유

성능 차이가 생기는 이유

구체적인 실행 시간은 컴파일러·런타임 버전, JIT 워밍업 여부, 하드웨어에 따라 크게 달라지므로 여기서는 숫자 대신 차이가 생기는 구조를 정리합니다. 직접 비교가 필요하다면 같은 머신에서 워밍업을 포함해 측정해 보는 것이 가장 정확합니다.

  • C++: std::vector<int>는 정수 값을 연속된 메모리에 그대로 저장하므로 순회 시 캐시 효율이 가장 좋고, 컴파일 시점 최적화도 적용됩니다.
  • Java: JIT 컴파일로 핫 루프는 빠르게 돌지만, ArrayList<Integer>는 요소마다 박싱된 Integer 객체를 참조하므로 원시 int[]보다 간접 참조가 한 단계 늘어납니다.
  • JavaScript: V8 같은 엔진은 정수만 담긴 배열을 내부적으로 압축된 형태로 최적화하지만, 타입이 섞이거나 구멍이 생기면 느린 표현으로 전환되어 성능을 예측하기 어렵습니다.
  • Python: 인터프리터가 요소마다 타입을 확인하고, list는 객체 포인터 배열이라 순수 Python 루프는 네 언어 중 가장 느린 편입니다. 대량 숫자 연산은 NumPy처럼 C로 구현된 라이브러리에 맡기는 것이 일반적입니다.

개발 생산성, 유지보수성, 팀 숙련도까지 고려하면 가장 빠른 언어가 항상 더 나은 선택은 아닙니다.

메모리 사용량 비교

100만 개 정수를 담을 때 C++ vector<int>는 요소당 4바이트로 약 4MB면 충분합니다. 반면 Python list는 포인터 배열에 더해 정수 객체 자체가 별도로 할당되고, Java ArrayList<Integer>도 참조와 Integer 객체 헤더가 추가되므로 같은 데이터를 담아도 여러 배의 메모리를 씁니다. JavaScript는 엔진이 배열을 어떤 내부 표현으로 저장하느냐에 따라 달라집니다. 메모리가 중요하다면 Python은 array 모듈이나 NumPy 배열, Java는 원시 타입 배열(int[])을 쓰는 것이 일반적인 해법입니다.


상황별로 언어 고르기

언어 선택 플로우차트

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++ 확장 호출 (pybind11 등으로 빌드한 모듈)
import my_cpp_module  # C++로 작성한 확장
# 순수 Python 루프: 원소마다 인터프리터가 타입 확인·객체 생성
def slow_sum_of_squares(arr):
    total = 0
    for x in arr:
        total += x * x
    return total
# C++ 구현: 루프 전체가 네이티브 코드로 실행
result = my_cpp_module.fast_sum_of_squares(arr)

주의할 점은 경계를 넘는 비용입니다. Python list를 C++ 함수에 넘기면 원소마다 Python 객체를 C++ 값으로 변환하는 복사가 일어나므로, 함수 호출 한 번에 하는 일이 작으면 변환 비용이 계산 이득을 잡아먹습니다. 확장 모듈은 “큰 덩어리의 일을 한 번에” 넘기도록 설계해야 하고, 숫자 배열이라면 NumPy 배열처럼 연속 메모리 버퍼를 그대로 공유하는 방식이 변환 비용이 거의 없습니다. 참고로 내장 sum()은 이미 C로 구현되어 있어서, 단순 합계라면 확장 모듈을 만들 이유가 없습니다. 장점:

  • Python의 생산성 + C++의 성능

  • 병목 구간만 최적화 단점:

  • 빌드 복잡도 증가

  • 디버깅 어려움


자료구조별 언어 비교 요약

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

다음 단계

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


자주 묻는 질문 (FAQ)

Q. JavaScript에서 Map과 일반 Object 중 무엇을 써야 하나요?

A. 키가 문자열이 아니거나(객체를 키로 쓰는 경우), 항목을 자주 추가·삭제하거나, size로 크기를 바로 알아야 한다면 Map이 적합합니다. Object는 키로 문자열과 심볼만 받지만 JSON 직렬화가 자연스럽기 때문에 설정값이나 API 응답 같은 단순 키-값 데이터에 잘 맞습니다. 두 구조 모두 삽입 순서로 순회되지만, Object는 정수처럼 보이는 키를 먼저 오름차순으로 정렬하므로 순서가 중요하다면 Map이 더 예측 가능합니다.


같이 보면 좋은 글