我知道JavaScriptunshift()
和push()
方法之间的区别是什么,但是我想知道时间复杂度有什么区别?
我想push()
方法是O(1),因为您只是将一个项目添加到数组的末尾,但是我不确定unshift()
方法,因为我想您必须“将”所有其他现有元素“向前移动”,并且我想那是O(log n)或O(n)?
我知道JavaScriptunshift()
和push()
方法之间的区别是什么,但是我想知道时间复杂度有什么区别?
我想push()
方法是O(1),因为您只是将一个项目添加到数组的末尾,但是我不确定unshift()
方法,因为我想您必须“将”所有其他现有元素“向前移动”,并且我想那是O(log n)或O(n)?
unshift
可以接近恒定时间,但是我想知道使常规数组访问复杂化是否值得。我个人认为我从未写过电话给unshift
。
Answers:
据我所知,JavaScript语言规范并未规定这些功能的时间复杂性。
当然可以用O(1)push
和unshift
操作实现类似数组的数据结构(O(1)随机访问)。C ++std::deque
就是一个例子。因此,使用C ++双端队列在内部表示Javascript数组的Javascript实现将具有O(1)push
和unshift
操作。
但是,如果您需要保证这样的时限,则必须自己动手,如下所示:
push()更快。
js>function foo() {a=[]; start = new Date; for (var i=0;i<100000;i++) a.unshift(1); return((new Date)-start)}
js>foo()
2190
js>function bar() {a=[]; start = new Date; for (var i=0;i<100000;i++) a.push(1); return((new Date)-start)}
js>bar()
10
上面没有考虑数组的顺序。如果要正确比较它们,则必须反转推入的数组。但是,10ms
使用此代码段,在chrome上,“推入后退”功能仍然快〜
var a=[];
var start = new Date;
for (var i=0;i<100000;i++) {
a.unshift(1);
}
var end = (new Date)-start;
console.log(`Unshift time: ${end}`);
var a=[];
var start = new Date;
for (var i=0;i<100000;i++) {
a.push(1);
}
a.reverse();
var end = (new Date)-start;
console.log(`Push and reverse time: ${end}`);
push(...)
速度提高了10%。我没想到JavaScript引擎之间会有如此大的差异。!(我刚刚意识到这个q已有2年历史了,而Safari已经走了很长一段路,我使用的Safari 7.1.6
是MacPro 2014模型。)
push
withshift
会比unshift
with更快pop
。
unshift
耗时8毫秒,push
耗时
对于那些对v8实现感到好奇的人,这里就是源。因为unshift
需要任意数量的参数,所以数组将自身移动以容纳所有参数。
UnshiftImpl
最终调用AddArguments
了start_position
的AT_START
它踢这个else
说法
// If the backing store has enough capacity and we add elements to the
// start we have to shift the existing objects.
Isolate* isolate = receiver->GetIsolate();
Subclass::MoveElements(isolate, receiver, backing_store, add_size, 0,
length, 0, 0);
并带到了MoveElements
。
static void MoveElements(Isolate* isolate, Handle<JSArray> receiver,
Handle<FixedArrayBase> backing_store, int dst_index,
int src_index, int len, int hole_start,
int hole_end) {
Heap* heap = isolate->heap();
Handle<BackingStore> dst_elms = Handle<BackingStore>::cast(backing_store);
if (len > JSArray::kMaxCopyElements && dst_index == 0 &&
heap->CanMoveObjectStart(*dst_elms)) {
// Update all the copies of this backing_store handle.
*dst_elms.location() =
BackingStore::cast(heap->LeftTrimFixedArray(*dst_elms, src_index))
->ptr();
receiver->set_elements(*dst_elms);
// Adjust the hole offset as the array has been shrunk.
hole_end -= src_index;
DCHECK_LE(hole_start, backing_store->length());
DCHECK_LE(hole_end, backing_store->length());
} else if (len != 0) {
WriteBarrierMode mode = GetWriteBarrierMode(KindTraits::Kind);
dst_elms->MoveElements(heap, dst_index, src_index, len, mode);
}
if (hole_start != hole_end) {
dst_elms->FillWithHoles(hole_start, hole_end);
}
}
我还想指出v8具有不同的概念,element kinds
具体取决于数组包含的内容。这也会影响性能。
实际上很难说出性能是什么,因为它实际上取决于传递的元素类型,数组中有多少孔等。如果我进一步研究一下,也许我可以给出确定的答案,但总的来说,我认为由于unshift
需要在数组中分配更多的空间,因此通常可以假设它是O(N)(将根据元素的数量线性缩放),但是如果我错了,请有人纠正我。