자바 스크립트 배열의 Big O


105

JavaScript의 배열은 항목을 추가하고 제거하여 수정하기가 매우 쉽습니다. 대부분의 언어 배열이 고정 크기이고 크기를 조정하려면 복잡한 작업이 필요하다는 사실을 다소가립니다. JavaScript를 사용하면 성능이 떨어지는 배열 코드를 쉽게 작성할 수 있습니다. 이것은 질문으로 이어집니다.

배열 성능과 관련하여 JavaScript 구현에서 어떤 성능 (큰 O 시간 복잡도 측면에서)을 기대할 수 있습니까?

모든 합리적인 JavaScript 구현에는 다음과 같은 큰 O가 있다고 가정합니다.

  • 액세스-O (1)
  • 추가-O (n)
  • 준비중-O (n)
  • 삽입-O (n)
  • 삭제-O (n)
  • 스와핑-O (1)

JavaScript를 사용하면 new Array(length)구문을 사용하여 배열을 특정 크기로 미리 채울 수 있습니다 . (보너스 질문 : O (1) 또는 O (n) 방식으로 배열을 생성합니까?) 이것은 기존 배열과 더 비슷하며 사전 크기 배열로 사용하면 O (1) 추가를 허용 할 수 있습니다. 순환 버퍼 로직이 추가되면 앞에 O (1)을 얻을 수 있습니다. 동적 확장 배열을 사용하는 경우 O (log n)는 두 가지 모두에 대한 평균 사례가됩니다.

여기에서 가정 한 것보다 더 나은 성능을 기대할 수 있습니까? 어떤 사양에도 설명되어 있지는 않지만 실제로는 모든 주요 구현이이면에서 최적화 된 어레이를 사용할 수 있습니다. 동적으로 확장되는 어레이 또는 다른 성능 향상 알고리즘이 작동합니까?

추신

이것이 궁금한 이유는 정렬 알고리즘을 연구하고 있기 때문이며, 대부분은 전체적인 큰 O를 설명 할 때 추가 및 삭제가 O (1) 작업이라고 가정하는 것 같습니다.


6
크기가있는 Array 생성자는 현대 JavaScript 구현에서 거의 쓸모가 없습니다. 단일 매개 변수 형식에서는 거의 아무것도하지 않습니다. (설정 .length되지만 그게 다입니다.) 배열은 일반 Object 인스턴스와 크게 다르지 않습니다.
Pointy

3
length속성을 설정하는 것과 공간을 미리 할당하는 것은 완전히 다른 두 가지입니다.
Pointy

1
@Pointy : 설정 이 O (1) array[5]일 때 너무 많이 기대하고 new Array(10)있습니까?
Kendall Frey

1
ECMAScript는 Array 객체가 구현되는 방법을 정의 하지 않지만 (일부 의미 규칙 만 정의 함) 예상되는 경우에 대해 다른 구현이 최적화 될 가능성이 매우 높습니다 (예 : 크기가 n보다 작은 배열에 대해 "실제 배열"지원을 가짐) ). 내가 구현에 그 정통한 아니지만, 할 것 정말 놀랐다 ...이 곳을하지 않은 경우

5
@KendallFrey "Best answer"는 다른 n / 액세스 패턴에 대한 jsperf 테스트 케이스를 작성하고 그 결과를 볼 수 있습니다 ;-)

답변:


111

참고 :이 답변은 2012 년에 정확했지만 오늘날 엔진은 객체와 배열 모두에 대해 매우 다른 내부 표현을 사용합니다. 이 대답은 사실 일 수도 있고 아닐 수도 있습니다.

배열을 사용하여 배열을 구현하는 대부분의 언어와 달리 Javascript 배열은 객체이며 값은 일반 객체 값과 마찬가지로 해시 테이블에 저장됩니다. 이와 같이 :

  • 액세스-O (1)
  • Appending-Amortized O (1) (때로는 해시 테이블의 크기를 조정해야하며 일반적으로 삽입 만 필요함)
  • Prepending-O (n) via unshift, 모든 인덱스를 재 할당해야하기 때문입니다.
  • 삽입-값이 존재하지 않는 경우 상각 된 O (1). O (n) 기존 값을 이동하려는 경우 (예 :) splice.
  • 삭제-상각 된 O (1)는 값을 제거하고, O (n)는를 통해 인덱스를 재 할당하려는 경우 splice입니다.
  • 스와핑-O (1)

일반적으로 dict에서 키를 설정하거나 설정 해제하는 것은 O (1)로 분할되며 인덱스가 무엇이든 상관없이 배열에도 동일하게 적용됩니다. 영향을받는 모든 값을 업데이트해야하기 때문에 기존 값의 번호를 다시 매겨 야하는 모든 작업은 O (n)입니다.


4
앞에 O (n)을 붙여서는 안 되나요? 모든 인덱스를 이동해야하기 때문입니다. 삽입 및 삭제도 동일합니다 (임의 인덱스에서 요소 이동 / 축소).
nhahtdh

2
또한 length배열 변형에 설정되어 get있습니까? 아니면 길이를 가져 와서 메모 할 수 있습니까?
alex

27
이 답변을 언급 할 가치가있는 것은 더 이상 정확하지 않습니다. 현대 엔진은 희소하지 않는 한 배열 (또는 인덱스 된 정수 키가있는 객체)을 해시 테이블로 저장하지 않습니다 (하지만 C에서와 같은 배열). 당신이 시작하기 위해 여기 설명하는 '고전적인'벤치 마크
벤자민 Gruenbaum을

4
이것은 표준에 의해 정의됩니까 아니면 JS 엔진의 일반적인 구현입니까? V8은 무엇입니까?
Albert

4
@BenjaminGruenbaum 저장 방법에 대해 조금 개발할 수 있다면 좋을 것입니다. 또는 몇 가지 출처를 제공하십시오.
Ced

1

보증

어레이 작업에 대해 지정된 시간 복잡도 보장은 없습니다. 어레이의 성능은 엔진이 선택한 기본 데이터 구조에 따라 다릅니다. 엔진은 또한 다른 표현을 가질 수 있으며 특정 휴리스틱에 따라 전환 할 수 있습니다. 초기 배열 크기는 그러한 휴리스틱 일 수도 있고 아닐 수도 있습니다.

현실

예를 들어, V8은 (오늘부터) 해시 테이블배열 목록 을 모두 사용 하여 배열 을 나타냅니다. 또한 객체에 대한 다양한 표현이 있으므로 배열과 객체를 비교할 수 없습니다. 따라서 배열 액세스는 항상 O (n)보다 낫고 C ++ 배열 액세스만큼 빠를 수도 있습니다. 데이터 구조의 크기에 도달하지 않고 크기를 조정해야하는 경우가 아니면 추가는 O (1)입니다 (O (n)). 준비하는 것이 더 나쁩니다. 삭제는 delete array[index]엔진이 표현을 변경하도록 강제 할 수 있으므로 (하지 마세요!) 와 같은 작업을 수행하면 훨씬 더 나빠질 수 있습니다 .

조언

숫자 데이터 구조에 배열을 사용하십시오. 그것이 그들이 의미하는 바입니다. 이것이 엔진이이를 최적화하는 것입니다. 희소 배열을 피하십시오 (또는 필요한 경우 성능 저하를 예상). 데이터 유형이 혼합 된 배열을 사용하지 마십시오 (내부 표현이 더 복잡해 지므로 ).

특정 엔진 (및 버전)에 대해 정말로 최적화하려면 소스 코드 에서 절대 답을 확인하십시오 .


잠시만 기다려주세요. 데이터 유형이 혼합 된 배열을 가질 수 있습니까? Javascript는 너무 멋지다!
Anurag

@Anurag 정확히,하지만 99 %의 경우이 기능이 필요하지 않을 것입니다
Desiigner
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.