JavaScript配列のビッグ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)は両方の平均ケースになります。

ここでの想定よりも、いくつかの点でより良いパフォーマンスを期待できますか?仕様で概説されているものはないと思いますが、実際には、すべての主要な実装が舞台裏で最適化された配列を使用している可能性があります。動的に拡張する配列または他のパフォーマンス向上アルゴリズムが機能していますか?

PS

私がこれを不思議に思っているのは、いくつかの並べ替えアルゴリズムを研究しているためです。それらのほとんどは、全体的な大きなOを記述するときに、追加と削除がO(1)操作であると想定しているようです。


6
サイズを持つ配列コンストラクターは、最新のJavaScript実装ではほとんど役に立ちません。その単一のパラメータ形式では、ほとんど何もしません。(設定されます.lengthが、それはそれで終わりです。)配列は、プレーンなObjectインスタンスとそれほど違いはありません。
2012

3
lengthプロパティの設定とスペースの事前割り当ては、2つのまったく異なるものです。
先のとがった

1
@Pointy:array[5]aの設定new Array(10)がO(1)であると期待しているときに、期待しすぎですか
Kendall Frey

1
ECMAScriptはArrayオブジェクトの実装方法を定義していませが(いくつかのセマンティックルールを定義しているだけです)、さまざまな実装が予想されるケースに対して最適化する可能性が非常に高くなります(たとえば、サイズがn未満の配列の「実際の配列」バッキングがある場合)。 )。私は実装にそれほど精通していませんが、これがどこかで行われなかった場合は本当に驚きます...

5
@KendallFrey "ベストアンサー"は、さまざまなn /アクセスパターンのjsperfテストケースをいくつか記述し、その結果を確認する可能性があります;-)

回答:


111

注:この回答は2012年に正しかったのですが、エンジンは現在、オブジェクトと配列の両方に非常に異なる内部表現を使用しています。この答えは正しい場合とそうでない場合があります。

JavaScriptで配列を使用して配列を実装するほとんどの言語とは対照的に、配列はオブジェクトであり、値は通常のオブジェクト値と同じようにハッシュテーブルに格納されます。など:

  • アクセス-O(1)
  • 追加-償却済みO(1)(ハッシュテーブルのサイズ変更が必要な場合があります。通常、挿入のみが必要です)
  • 先頭に追加- unshiftすべてのインデックスを再割り当てする必要があるため、を介したO(n)
  • 挿入-値が存在しない場合は、償却されたO(1)。既存の値をシフトする場合はO(n)(例:を使用splice)。
  • 削除-値を削除するために償却されたO(1)、を介してインデックスを再割り当てする場合はO(n)splice
  • スワッピング-O(1)

一般に、dictでキーを設定または設定解除すると、O(1)が償却されます。これは、インデックスが何であるかに関係なく、配列でも同じです。影響を受けるすべての値を更新する必要があるため、既存の値の再番号付けが必要な操作はすべてO(n)です。


4
先頭にO(n)を付けるべきではありませんか?すべてのインデックスをシフトする必要があるため。挿入と削除についても同様です(任意のインデックスで、要素のシフト/折りたたみ)。
nhahtdh

2
また、length配列変異に設定されてgetいますか、それとも長さが取得され、おそらくそれをメモするのですか?
アレックス、

27
この答えに言及する価値はもはやありません。最近のエンジンは、スパースでない限り、配列(またはインデックス付き整数キーを持つオブジェクト)をハッシュテーブル(ただし、Cのような配列)として格納しません。ここ
Benjamin Gruenbaum 2013年

4
これは標準で定義されていますか、それともJSエンジンの一般的な実装ですか?V8とは何ですか?
アルバート

4
@BenjaminGruenbaumあなたがそれらがどのように保存されるかについて少し開発できたら素晴らしいでしょう。または、いくつかのソースを提供します。
Ced

1

保証

アレイの操作については、時間の複雑さの保証はありません。配列の実行方法は、エンジンが選択する基本的なデータ構造によって異なります。エンジンも異なる表現を持ち、特定のヒューリスティックに応じてそれらを切り替える場合があります。初期配列サイズは、このようなヒューリスティックな場合とそうでない場合があります。

現実

たとえば、V8は(現在のところハッシュテーブル配列リストの両方を使用して配列を表します。また、オブジェクトのさまざまな表現があるため、配列とオブジェクトを比較することはできません。したがって、アレイへのアクセスは、常により良いO(N)などであり、可能性があるにもC ++配列アクセスの速さです。データ構造のサイズに達し、それをスケーリングする必要がある場合を除いて、追加はO(1)です(これはO(n)です)。プリペンディングはさらに悪い。delete array[index]エンジンが強制的に表現を変更する可能性があるため、削除しないでください。

助言

数値データ構造には配列を使用します。それが彼らが意図していることです。それがエンジンがそれらを最適化するものです。スパース配列は避けてください(必要な場合は、パフォーマンスの低下を期待してください)。データ型が混在する配列は避けてください(内部表現がより複雑になるため)。

特定のエンジン(およびバージョン)を最適化したい場合は、そのソースコードで絶対的な答えを確認してください。


ちょっと待ってください、データ型が混在する配列を持つことができますか?JavaScriptはとてもクールです!
アヌラグ

@Anurag正確に、しかし、例99%で、あなたは、この機能必要はありません
Desiigner
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.