STL의 벡터 대 목록


238

효과적인 STL에서

벡터는 기본적으로 사용해야하는 시퀀스 유형입니다.

무슨 뜻입니까? 효율성을 무시하면 vector아무것도 할 수 없는 것 같습니다 .

아무도 vector실현 가능한 옵션이 아니지만 list사용해야 하는 시나리오를 제공 할 수 있습니까?


6
요청한 것은 아니지만 벡터를 기본값으로 설정하면 벡터가 포인터의 "전통적인"동적 배열 주위의 얇은 래퍼이기 때문에 이전 코드, C 라이브러리 또는 템플릿이 아닌 라이브러리와 쉽게 상호 작용할 수 있음을 의미합니다. 그리고 크기.

18
Bjarne Strostrup은 실제로 임의의 숫자를 생성 한 다음 각각 목록과 벡터에 추가 한 테스트를 수행했습니다. 목록 / 벡터가 항상 순서대로 삽입되었습니다. 이것이 일반적으로 "목록 도메인"이지만 벡터는 목록보다 큰 마진을 능가합니다. 메모리 액세스 속도가 느리고 순차 데이터에 대해 캐싱이 더 나은 이유입니다. 그것은 "GoingNative 2012 '에서 기조 연설에서 모두 사용할 수
회피


1
@evading이 언급 한 Bjarne Stroustrup의 기조 연설을보고 싶다면 여기에서 찾았습니다. youtu.be/OB-bdWKwXsU?t=2672
brain56

답변:


98

시퀀스의 끝을 제외한 모든 위치에 반복적으로 많은 항목을 삽입하려는 상황.

각기 다른 유형의 컨테이너에 대한 복잡성 보장을 확인하십시오.

표준 컨테이너의 복잡성 보장은 무엇입니까?


2
끝에 요소를 삽입하면 메모리 할당 및 요소 복사 비용이 발생할 수 있으므로 계산에 포함됩니다. 또한, 벡터의 시작에 elenets를 삽입하는 것은 거의 불가능하다, listpush_front
Notinlist

9
아니요, 벡터 끝에 요소를 삽입하는 것은 일정한 시간으로 상각됩니다. 메모리 할당은 가끔씩 만 발생하며 벡터를 미리 할당하여 벡터를 방지 할 수 있습니다. 당신이 경우 물론, 반드시 일관 일정한 시간 삽입을 보장 한,이 여전히 문제가되는 것 같아요.
Brian

16
@Notinlist-다음은 "불가능한 것"입니까? v.insert (v.begin (), i)
Manuel

5
@Notinlist-동의합니다 .OP가 (성능) 발로 자신을 촬영하려는 경우 OP가 인터페이스가 없다고 생각하지 않기를 바랄뿐입니다.
Manuel

16
Bjarne Strostrup은 실제로 임의의 숫자를 생성 한 다음 각각 목록과 벡터에 추가 한 테스트를 수행했습니다. 목록 / 벡터가 항상 순서대로 삽입되었습니다. 이것이 일반적으로 "목록 도메인"이지만 벡터는 목록보다 큰 마진을 능가합니다. 메모리 액세스 속도가 느리고 순차 데이터에 대해 캐싱이 더 나은 이유입니다. 그것은 "GoingNative 2012 '에서 기조 연설에서 모두 사용할 수
회피

409

벡터:

  • 연속 메모리.
  • 미래 요소를위한 공간을 미리 할당하므로 요소 자체에 필요한 것 이상의 추가 공간이 필요합니다.
  • 각 요소에는 요소 유형 자체에 대한 공간 만 필요합니다 (추가 포인터 없음).
  • 요소를 추가 할 때마다 전체 벡터에 대한 메모리를 다시 할당 할 수 있습니다.
  • 끝에 삽입은 일정하고 상각 된 시간이지만 다른 곳에서는 삽입에 비용이 많이 드는 O (n)입니다.
  • 벡터 끝의 소거는 일정한 시간이지만 나머지는 O (n)입니다.
  • 해당 요소에 무작위로 액세스 할 수 있습니다.
  • 벡터에 요소를 추가하거나 제거하면 반복자가 무효화됩니다.
  • 요소 배열이 필요한 경우 기본 배열을 쉽게 얻을 수 있습니다.

명부:

  • 비 연속 메모리
  • 사전 할당 된 메모리가 없습니다. 목록 자체의 메모리 오버 헤드는 일정합니다.
  • 각 요소에는 목록의 다음 및 이전 요소에 대한 포인터를 포함하여 요소를 보유하는 노드를위한 추가 공간이 필요합니다.
  • 요소를 추가한다고해서 전체 목록에 메모리를 다시 할당 할 필요는 없습니다.
  • 삽입과 소거는 목록에서 어디서나 발생하더라도 저렴합니다.
  • 목록과 스 플라이 싱을 결합하는 것이 저렴합니다.
  • 요소에 무작위로 액세스 할 수 없으므로 목록에서 특정 요소를 얻는 데 많은 비용이 듭니다.
  • 반복자는 목록에서 요소를 추가하거나 제거하더라도 유효합니다.
  • 요소의 배열이 필요한 경우 기본 배열이 없으므로 새 요소를 작성하고 모두 추가해야합니다.

일반적으로 사용중인 순차 컨테이너 유형에 신경 쓰지 않을 때 벡터를 사용하지만 컨테이너 이외의 다른 곳에서 많은 삽입 또는 삭제를 수행하는 경우 벡터를 사용하십시오. 목록을 사용하십시오. 또는 임의 액세스가 필요한 경우 목록이 아닌 벡터를 원할 것입니다. 그 외에는 응용 프로그램에 따라 자연스럽게 하나 또는 다른 것이 필요한 경우가 있지만 일반적으로 좋은 지침입니다.


2
또한 무료 상점에서 할당하는 것은 무료가 아닙니다. :) 벡터에 새 항목을 추가하면 O (log n) 무료 저장소 할당이 수행되지만 reserve()이를 O (1)로 줄이기 위해 호출 할 수 있습니다 . 목록에 새 항목을 추가하면 (즉, 연결하지 않음) O (n) 무료 저장소 할당이 수행됩니다.
bk1e

7
또 다른 고려 사항은 list요소를 지울 때 메모리 를 비우지 만 vector그렇지는 않습니다. 트릭 vector을 사용하지 않는 한 A 는 크기를 줄일 때 용량을 줄이지 않습니다 swap().
bk1e

@ bk1e : reserve ()와 swap () :)에서 여러분의 트릭을 알고 싶습니다
Dzung Nguyen

2
@nXqd : 벡터에 N 개의 요소를 추가해야하는 경우 v.reserve (v.size () + N)을 호출하여 하나의 빈 저장소 할당 만 수행합니다. swap () 트릭은 다음과 같습니다. stackoverflow.com/questions/253157/how-to-downsize-stdvector
bk1e

1
@simplename 아니오. 올바른 말입니다. 벡터는 현재 벡터에있는 요소의 공간을 넘어 여분의 공간을 할당합니다. 그런 다음 여분의 용량은 벡터를 성장시키는 데 사용되어 벡터가 성장하면 상각되는 O (1)입니다.
Jonathan M Davis

35

요소를 자주 삽입 할 필요가 없으면 벡터가 더 효율적입니다. 목록보다 CPU 캐시 지역성이 훨씬 뛰어납니다. 즉, 하나의 요소에 접근하는 것은 그것을 만드는 아주 다음 요소가 캐시에 존재하고 느린 RAM을 읽을 필요없이 검색 할 수 가능성이 높습니다.


32

여기에있는 대부분의 답변에는 중요한 세부 사항이 하나 없습니다.

컨테이너에 무엇을 보관하고 싶습니까?

ints 모음 인 std::list경우 재 할당 할 수 있는지 여부에 관계없이 모든 시나리오에서 잃어 버릴 수 있습니다. 앞면에서만 제거합니다. list<int>뛰는 예제를 준비하는 것은 매우 어려울 것 vector<int>입니다. 그리고 그렇다하더라도, deque<int>크지 메모리 오버 헤드를해야합니다 목록의 사용을 justyfing, 더 나은 또는 가까운 수 있습니다.

그러나 대량의 추악한 데이터를 처리하고 그중 일부를 처리하는 경우 재 할당으로 인해 삽입 및 복사 할 때 전체적으로 할당하지 않으려는 경우 재앙이 될 수 있습니다. list<UglyBlob>보다 vector<UglyBlob>.

그래도 vector<UglyBlob*>또는로 전환하면 vector<shared_ptr<UglyBlob> >다시-목록이 뒤쳐집니다.

따라서 액세스 패턴, 대상 요소 수 등은 여전히 ​​비교에 영향을 미치지 만 내 견해로는 요소 크기-복사 비용 등이 있습니다.


1
마이어스에 의해 "효과적인 STL"을 읽을 때 내가 가진 또 하나의 반사 :의 독특한 속성 list<T>에 대한 가능성 spliceO (1) . 일정한 시간의 접속이 필요한 경우,리스트는 선택의 구조 일 수도 있습니다;)
Tomasz Gandor

+1- UglyBlob문자열 멤버가 몇 개인 개체라도 복사 비용이 매우 비싸기 때문에 재 할당 비용 많이 듭니다. 또한 : vector수십 바이트 크기 의 보유 객체가 기하 급수적으로 증가 할 수있는 공간 오버 헤드를 무시하지 마십시오 ( reserve미리 할 수없는 경우 ).
Martin Ba

에 관해서는 vector<smart_ptr<Large>>list<Large>- 난 당신이 요소에 랜덤 액세스가 필요한 경우는,라고 말하고 싶지만 vector의미가 있습니다. 임의 액세스가 필요하지 않은 경우에는 list더 단순 해 보이며 동일하게 수행됩니다.
Martin Ba

19

std :: list의 특별한 기능 중 하나는 스 플라이 싱 (일부 또는 전체 목록을 링크하거나 다른 목록으로 이동)입니다.

또는 내용이 복사하는 데 비용이 많이 드는 경우 일 수 있습니다. 이러한 경우 컬렉션을 목록으로 정렬하는 것이 더 저렴할 수 있습니다.

또한 모음이 작고 내용을 복사하는 데 비용이 많이 들지 않으면 어디에서나 삽입하고 지우더라도 벡터가 여전히 목록보다 성능이 우수 할 수 있습니다. 목록은 각 노드를 개별적으로 할당하며 몇 개의 간단한 객체를 옮기는 것보다 비용이 많이들 수 있습니다.

나는 매우 어려운 규칙이 있다고 생각하지 않습니다. 컨테이너와 관련하여 주로 수행하려는 작업과 컨테이너의 크기 및 포함 된 유형에 따라 다릅니다. 벡터는 일반적으로 그 내용을 단일 연속 블록으로 할당하기 때문에 목록보다 우선합니다 (기본적으로 동적으로 할당 된 배열이며 대부분의 경우 배열은 많은 것들을 보유하는 가장 효율적인 방법입니다).


1
+1. 접합이 간과되었지만 불행히도 원하는 시간이 일정하지 않습니다. : ((((list :: size가 상수 시간이면 불가능합니다.)

이 이유 때문에 list :: size는 선형 적이라고 확신합니다.
UncleBens

1
@Roger : list::size반드시 일정한 시간은 아닙니다. 참조 stackoverflow.com/questions/228908/is-listsize-really-ongcc.gnu.org/ml/libstdc++/2005-11/msg00219.html
Potatoswatter

@ Potatoswatter : 표준이 모호하여 결과적으로 "호환되는"구현에 의존 할 수 없다는 점은 더 많은 문제를 야기합니다. 휴대 가능하고 신뢰할 수있는 보증을 받으려면 문자 그대로 stdlib를 피해야합니다.

@ 로거 : 예, 불행히도. 현재 프로젝트는 스플 라이스 작업에 크게 의존하며 그 구조는 거의 C입니다. 불행히도 N3000 splice에서 다른 목록 간의 시퀀스 는 선형 복잡도로 지정되며 size특히 일정합니다. 따라서 반복하는 초보자를 수용 size하기 위해 STL 또는 "호환되는"컨테이너 기간 동안 전체 알고리즘 클래스에 도달 할 수 없습니다.
Potatoswatter

13

글쎄, 내 수업의 학생들은 벡터를 사용하는 것이 더 효과적 일 때 나에게 설명 할 수없는 것처럼 보이지만 목록을 사용하도록 조언 할 때 그들은 매우 행복해 보입니다.

이것이 내가 이해하는 방법입니다

목록 : 각 항목에는 다음 또는 이전 요소에 대한 주소가 포함되어 있으므로이 기능을 사용하면 정렬되지 않아도 항목을 무작위로 지정할 수 있습니다. 순서가 변경되지 않습니다. 메모리가 조각난 경우 효율적입니다. 그러나 또 다른 큰 장점이 있습니다. 포인터를 변경하면 항목을 쉽게 삽입 / 제거 할 수 있습니다. 단점 : 임의의 단일 항목을 읽으려면 올바른 주소를 찾을 때까지 한 항목에서 다른 항목으로 이동해야합니다.

벡터 : 벡터를 사용할 때 메모리는 일반 배열처럼 훨씬 더 체계적으로 구성됩니다. 각 n 번째 항목은 (n-1) 번째 항목 바로 뒤에 그리고 (n + 1) 번째 항목 앞에 저장됩니다. 왜 목록보다 낫습니까? 빠른 임의 액세스를 허용하기 때문입니다. 방법은 다음과 같습니다. 벡터에서 항목의 크기를 알고 메모리에서 연속적인 경우 n 번째 항목의 위치를 ​​쉽게 예측할 수 있습니다. 원하는 항목을 읽기 위해 목록의 모든 항목을 찾아 볼 필요는 없습니다. 벡터를 사용하면 목록을 직접 읽을 수없고 목록을 읽을 수 없습니다. 반면에 벡터 배열을 수정하거나 값을 변경하면 속도가 훨씬 느려집니다.

메모리에 추가 / 제거 할 수있는 개체를 추적하는 데 목록이 더 적합합니다. 대량의 단일 항목에서 요소에 액세스하려는 경우 벡터가 더 적합합니다.

목록이 어떻게 최적화되는지는 모르겠지만 빠른 읽기 액세스를 원한다면 벡터를 사용해야합니다 .STL이 목록을 얼마나 잘 작성하면 벡터보다 읽기 액세스가 빠르지 않기 때문입니다.


"벡터 배열을 수정하거나 값을 변경하는 것이 훨씬 느리다"-이것은 벡터가 낮은 수준의 연속적인 특성으로 인해 우수한 성능을 기대하는 벡터에 대해 이전에 말한 것과 모순되는 것 같습니다. 크기 를 변경 하여 원인 을 재 할당 하는 것이 느릴 수 있다는 의미입니까? 그런 다음 동의했지만 사용할 수있는 경우 이러한 문제를 피할 수 있습니다. vectorreserve()
underscore_d

10

기본적으로 벡터는 자동 메모리 관리 기능이있는 배열입니다. 데이터가 메모리에 인접 해 있습니다. 중간에 데이터를 삽입하는 것은 비용이 많이 드는 작업입니다.

목록에서 데이터는 관련없는 메모리 위치에 저장됩니다. 중간에 삽입하는 것은 새로운 데이터를위한 공간을 만들기 위해 일부 데이터를 복사하는 것을 포함하지 않습니다.

더 구체적으로 귀하의 질문에 대답하기 위해이 페이지를 인용 하겠습니다

벡터는 일반적으로 요소에 액세스하고 시퀀스의 끝에서 요소를 추가하거나 제거하는 데 가장 효율적입니다. 끝 이외의 위치에 요소를 삽입하거나 제거하는 작업의 경우 deques 및 list보다 성능이 저하되고 목록보다 일관된 반복자 및 참조가 적습니다.


9

언제든지 반복자를 무효화 할 수 없습니다.


2
그러나 지속 여부를 묻지 않고 반복자에 대한 결론으로 이동하지 참조 으로는 deque충분합니다.
Potatoswatter

8

시퀀스 중간에 많은 삽입 또는 삭제가있는 경우 예를 들어 메모리 관리자.


그들 사이의 차이점은 단지 기능적 문제가 아니라 효율성입니다.
skydoor

둘 다 물론 일련 의 요소를 모델링합니다 . @dirkgently에서 언급했듯이 사용법에는 약간의 차이가 있지만 선택할 시퀀스를 결정하기 위해 "종종 완료된"작업의 복잡성을 살펴 봐야합니다 (@Martin answer).
AraK

@skydoor-몇 가지 기능 차이가 있습니다. 예를 들어, 벡터 만 랜덤 액세스를 지원합니다 (즉, 인덱스 화 가능).
Manuel

2
@skydoor : 효율성은 성능으로 변환됩니다. 성능이 저하되면 기능이 손상 될 수 있습니다. 결국 성능은 C ++의 장점입니다.
Potatoswatter

4

반복자의 유효성을 유지하는 것이 목록을 사용하는 한 가지 이유입니다. 다른 하나는 항목을 푸시 할 때 벡터를 재할 당하지 않으려는 경우입니다. 이것은 reserve ()를 지능적으로 사용하여 관리 할 수 ​​있지만 경우에 따라 목록을 사용하는 것이 더 쉬울 수도 있고 가능할 수도 있습니다.


4

컨테이너간에 객체를 이동하려면을 사용할 수 있습니다 list::splice.

예를 들어, 그래프 분할 알고리즘은 증가하는 수의 컨테이너 사이에 일정한 수의 객체를 재귀 적으로 나눌 수있다. 객체는 한 번 초기화되고 항상 메모리의 동일한 위치에 유지되어야합니다. 재 할당보다 재 링크하여 재배 열하는 것이 훨씬 빠릅니다.

편집 : 라이브러리가 C ++ 0x를 구현할 준비를 할 때 하위 시퀀스를 목록에 스 플라이 싱하는 일반적인 경우는 시퀀스 길이에 따라 선형 적으로 복잡해지고 있습니다. 그 이유는 splice현재 요소의 수를 계산하기 위해 시퀀스를 반복해야하기 때문입니다. (목록의 크기를 기록해야하기 때문에) 단순히 목록을 계산하고 다시 연결하는 것이 여전히 다른 방법보다 빠르며 전체 목록 또는 단일 요소를 연결하는 것은 지속적인 복잡성을 가진 특수한 경우입니다. 그러나 결합 할 시퀀스가 ​​긴 경우 더 나은 구식 비준수 컨테이너를 찾아야합니다.


2

유일한 어려운 규칙 list사용해야 은 컨테이너의 요소에 포인터를 배포해야하는 경우입니다.

와 달리 vector요소의 메모리는 재 할당되지 않습니다. 가능하다면 사용하지 않는 메모리에 대한 포인터를 가질 수 있습니다 SEGFAULT.

(기술적으로 vector*_ptr또한 것 일하지만 경우에 당신은 모방하는list 것을 그냥 의미 그래서.)

다른 소프트 규칙은 컨테이너 중간에 요소를 삽입 할 때 발생할 수있는 성능 문제와 관련 list이 있으므로 바람직합니다.


2

간단하게
-C ++에서 컨테이너를 선택하는 것이 혼란 스러울 때이 흐름도 이미지를 사용하십시오 (저에게 감사하십시오) :- 여기에 이미지 설명을 입력하십시오

벡터 -1. 벡터는
전염성 메모리를 기반으로합니다
. 2. 벡터는 작은 데이터 세트로가는 길입니다
3. 벡터는 데이터 세트를 순회하는 동안 가장 빠르게 수행됩니다
. 4. 벡터 삽입 삭제는 거대한 데이터 세트에서 느리지 만 매우 작은 경우에는 빠릅니다.

- 온라인
1. 목록이 힙 메모리를 기반으로
2. 목록은 매우 큰 데이터 세트에 대한 갈 방법은
3. 목록 거대한 데이터 세트에서 빠른 작은 데이터 세트 만 횡단에 느린 비교적 인
에 빠른 4. 목록 삽입 삭제됩니다 거대한 데이터 세트이지만 작은 데이터 세트에서는 느립니다.


1

목록은 stl의 이중 연결 목록에 대한 래퍼 일 뿐이므로 d- 링크 목록, 즉 O (1) 삽입 및 삭제에서 기대할 수있는 기능을 제공합니다. 벡터는 역동적 인 배열처럼 작동하는 전염성이있는 데이터 시퀀스입니다.


0

벡터리스트 의 경우 나에게 튀어 나오는 주요 차이점은 다음과 같습니다.

벡터

  • 벡터는 해당 요소를 연속 메모리에 저장합니다. 따라서 벡터 내에서 임의 액세스가 가능합니다. 즉, 기본 요소에 항목 인덱스를 곱하여 해당 요소에 액세스하기 때문에 벡터 요소에 액세스하는 것이 매우 빠릅니다. 실제로이 목적을 위해서는 O (1) 또는 일정한 시간 만 걸립니다.

  • 벡터는 기본적으로 배열을 래핑하기 때문에 벡터 (동적 배열)에 요소를 삽입 할 때마다 시간이 많이 걸리는 새로운 요소를 수용 할 수있는 새로운 연속 메모리 블록을 찾아서 크기를 조정해야합니다.

  • 그 안에 다른 요소에 대한 포인터를 저장하는 데 추가 메모리를 소비하지 않습니다.

명부

  • 목록은 비 연속 메모리에 해당 요소를 저장합니다. 따라서 목록 내에서 임의 액세스가 불가능합니다. 즉, 요소에 액세스하려면 포인터를 사용하고 벡터에 비해 느린 목록을 탐색해야합니다. O (n) 또는 선형 시간이 걸리며 O (1)보다 느립니다.

  • 리스트는 비 연속 메모리를 사용하기 때문에, 메모리의 재 할당을 피하기 때문에리스트에 요소를 삽입하는 데 걸리는 시간은 벡터의 경우보다 훨씬 효율적입니다.

  • 특정 요소 전후에 요소에 대한 포인터를 저장하기 위해 추가 메모리를 소비합니다.

따라서 이러한 차이를 염두에두고 주어진 시나리오에서 벡터 대 목록 의 승자를 결정하기 위해 일반적으로 메모리 , 자주 사용하는 임의 액세스삽입 을 고려 합니다 .


0

List는 이중 연결 목록이므로 요소를 쉽게 삽입하고 삭제할 수 있습니다. 벡터에서 요소를 중간에 삽입하려면 각 요소를 하나의 인덱스만큼 이동 해야하는 경우 몇 가지 포인터를 변경해야합니다. 또한 벡터의 크기가 가득 찬 경우 먼저 크기를 늘려야합니다. 따라서 비용이 많이 드는 작업입니다. 따라서 삽입 및 삭제 작업이 더 자주 수행되어야하는 경우에는 이러한 사례 목록을 사용해야합니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.