Big O of JavaScript arrays
Solution 1
NOTE: While this answer was correct in 2012, engines use very different internal representations for both objects and arrays today. This answer may or may not be true.
In contrast to most languages, which implement arrays with, well, arrays, in Javascript Arrays are objects, and values are stored in a hashtable, just like regular object values. As such:
- Access - O(1)
- Appending - Amortized O(1) (sometimes resizing the hashtable is required; usually only insertion is required)
- Prepending - O(n) via
unshift
, since it requires reassigning all the indexes - Insertion - Amortized O(1) if the value does not exist. O(n) if you want to shift existing values (Eg, using
splice
). - Deletion - Amortized O(1) to remove a value, O(n) if you want to reassign indices via
splice
. - Swapping - O(1)
In general, setting or unsetting any key in a dict is amortized O(1), and the same goes for arrays, regardless of what the index is. Any operation that requires renumbering existing values is O(n) simply because you have to update all the affected values.
Solution 2
guarantee
There is no specified time complexity guarantee for any array operation. How arrays perform depends on the underlying datastructure the engine chooses. Engines might also have different representations, and switch between them depending on certain heuristics. The initial array size might or might not be such an heuristic.
reality
For example, V8 uses (as of today) both hashtables and array lists to represent arrays. It also has various different representations for objects, so arrays and objects cannot be compared. Therefore array access is always better than O(n), and might even be as fast as a C++ array access. Appending is O(1), unless you reach the size of the datastructure and it has to be scaled (which is O(n)). Prepending is worse. Deletion can be even worse if you do something like delete array[index]
(don't!), as that might force the engine to change its representation.
advice
Use arrays for numeric datastructures. That's what they are meant for. That's what engines will optimize them for. Avoid sparse arrays (or if you have to, expect worse performance). Avoid arrays with mixed datatypes (as that makes internal representations more complex).
If you really want to optimize for a certain engine (and version), check its sourcecode for the absolute answer.
Kendall Frey
I am primarily a C# programmer, but also use JavaScript, and some other languages in my spare time. Website: http://kendallfrey.com
Updated on October 14, 2020Comments
-
Kendall Frey over 3 years
Arrays in JavaScript are very easy to modify by adding and removing items. It somewhat masks the fact that most languages arrays are fixed-size, and require complex operations to resize. It seems that JavaScript makes it easy to write poorly performing array code. This leads to the question:
What performance (in terms of big O time complexity) can I expect from JavaScript implementations in regards to array performance?
I assume that all reasonable JavaScript implementations have at most the following big O's.
- Access - O(1)
- Appending - O(n)
- Prepending - O(n)
- Insertion - O(n)
- Deletion - O(n)
- Swapping - O(1)
JavaScript lets you pre-fill an array to a certain size, using
new Array(length)
syntax. (Bonus question: Is creating an array in this manner O(1) or O(n)) This is more like a conventional array, and if used as a pre-sized array, can allow O(1) appending. If circular buffer logic is added, you can achieve O(1) prepending. If a dynamically expanding array is used, O(log n) will be the average case for both of those.Can I expect better performance for some things than my assumptions here? I don't expect anything is outlined in any specifications, but in practice, it could be that all major implementations use optimized arrays behind the scenes. Are there dynamically expanding arrays or some other performance-boosting algorithms at work?
P.S.
The reason I'm wondering this is that I'm researching some sorting algorithms, most of which seem to assume appending and deleting are O(1) operations when describing their overall big O.
-
Pointy almost 12 yearsThe Array constructor with a size is pretty much useless in modern JavaScript implementations. It does almost nothing at all in that single parameter form. (It sets
.length
but that's about it.) Arrays are really not much different from plain Object instances. -
Pointy almost 12 yearsSetting the
length
property and pre-allocating space are two completely different things. -
Kendall Frey almost 12 years@Pointy: Am I expecting too much when I expect setting
array[5]
on anew Array(10)
is O(1)? -
Pointy almost 12 yearsIt's probably not quite O(1), but it's like adding an element to a hashtable more than it is a linear array re-allocation.
-
Kendall Frey almost 12 yearsOuch. Really? Is there no way to pre-size an array at all?
-
Admin almost 12 yearsWhile the ECMAScript does not define how an Array object is implemented (it only defines some semantic rules), it is very possible that different implementations will optimize for expected cases (e.g. have a "real array" backing for arrays less than some n in size). I am not that savvy on implementations, but would be really surprised if this was not done somewhere ...
-
Kendall Frey almost 12 years@pst: That's the viewpoint I had.
-
Admin almost 12 years@KendallFrey "Best answer" is likely to write some jsperf test-cases for different n / access patterns and see what comes of it ;-)
-
argentage almost 12 yearsIt would be surprising to see an array which had O(n) for both appending and prepending.
-
Ced almost 7 years@Pointy setting the length property will prealocate space.
let a = []; a.length = 12
will create an array ofundefined
x 12. Did I misunderstand you comment ? -
Pointy almost 7 years@Ced it's not necessarily the case that setting the
length
property will actually allocate storage space. For example, try a test program that initializes 1000 arrays and sets thelength
property of each one to2000000000
. -
Michael Grazebrook over 3 years@Ced An implementation which wanted to optimise for cases like that could use a sparse array (array of arrays) so only those sections of the array which are used ever get allocated.
-
nhahtdh almost 12 yearsShouldn't prepend be O(n)? Since all the indices need to be shifted. Same for insertion and deletion (at arbitrary index, and shift/collapse the elements).
-
user3167101 almost 12 yearsAlso, is
length
set on the Array mutation, or is theget
on it going to get the length and possibly memoize it? -
Nick Johnson almost 12 years@nhahtdh Javascript doesn't have 'prepend', 'insert', or 'delete' operations that reassign array indices. It does have a 'splice' operation which does that, and is (obviously) O(n).
-
Nick Johnson almost 12 yearsFor
length
, see here. -
nhahtdh almost 12 years@NickJohnson: There is prepend:
unshift
. I can't see how your answer has anything to do with the question. -
Nick Johnson almost 12 years@nhahtdh Fair enough, I missed unshift. I don't know why you're having difficulty relating my answer to the question - it seems like a pretty straightforward thing to me.
-
nhahtdh almost 12 years@NickJohnson: I mentioned this in my first comment: I don't get how insertion and deletion are O(1) amortized. The only kind of "insert" and "delete" that can have O(1) amortized complexity is assigning value to the array.
-
Benjamin Gruenbaum over 10 yearsWorth mentioning this answer is no longer correct. Modern engines do not store Arrays (or objects with indexed integer keys) as hashtables (but like well... arrays like in C) unless they're sparse. To get you started here is a 'classical' benchmark illustrating this
-
Kendall Frey over 10 years@BenjaminGruenbaum As far as I know, this doesn't affect the complexity or behaviour, only the constant factor (which is beyond the scope of the question).
-
Benjamin Gruenbaum over 10 yearsYou're right, this answer corrects incorrect information that is beyond the scope of the question. I just wanted to point that out - didn't downvote or anything.
-
badunk over 10 yearsCan we get a reference to this? I'd love to learn more about how this was derived.
-
Nick Johnson over 10 years@badunk The values are all just those for a hashtable, which you can look up big-O analysis for on Wikipedia or elsewhere.
-
Albert about 10 yearsIs this defined by the standard or is this just a common implementation in JS engines? What's about V8?
-
Ced almost 7 years@BenjaminGruenbaum it would be nice if you could develop a bit on how they are stored. Or give some sources.
-
Squirrl about 6 yearsI'm still confused. Is new Array(100000) slower than new Array(10) ?
-
Andy Hayden about 5 yearsIs it fair to say this means it acts like an efficient stack/FIFO (but not as a queue/FILO)?
-
Andy Hayden about 5 years@BenjaminGruenbaum so what's the answer now?
-
Noitidart about 5 yearsWhat does amortized mean? So does this mean deletion via
.pop
is O(1)? -
Anurag Baundwal almost 4 yearsWait for a second, we can have arrays with mixed datatypes? Javascript is so cool!
-
Desiigner almost 4 years@Anurag exactly, but in 99% of cases you wouldn't need this feature
-
grreeenn about 3 years@Jonas-Wilms, could you please elaborate on the
unless you reach the size of the datastructure and it has to be scaled
part? We don't usually set array length on array creation in JS, does it mean that appending to any array created the regular way (likelet a = [1,2,3]
) requires datastructure scaling? -
Jonas Wilms about 3 years@grreeenn I guess not, the engine will probably directly allocate an array list with larger size