Javascript设置与阵列性能

时间:2016-08-17 23:15:45

标签: javascript arrays performance set iteration

这可能是因为集合对于Javascript来说相对较新,但是我无法在StackO或其他任何地方找到一篇文章来讨论Javascript中两者之间的性能差异。那么,两者之间在性能方面有什么不同?具体来说,就删除,添加和迭代而言。

7 个答案:

答案 0 :(得分:69)

好的,我测试过添加,迭代和删除数组和集合中的元素。我运行了一个“小”测试,使用10万个元素和一个“大”测试,使用10万个元素。结果如下。

将元素添加到集合

似乎.push数组方法比.add set方法快4倍,无论添加的元素数量是多少。

迭代并修改集合中的元素

对于测试的这一部分,我使用for循环迭代数组,并使用for of循环迭代整个集合。再次,迭代数组更快。这一次似乎是指数级的,因为它在“小”测试期间需要两倍的时间,而在“大”测试期间需要几乎四倍。

从集合中删除元素

现在这是它变得有趣的地方。我使用for循环和.splice的组合从数组中删除了一些元素,并使用for of.delete从集合中删除了一些元素。对于“小”测试,从集合中删除项目的速度提高了大约三倍(2.6毫秒vs 7.1毫秒),但“大”测试的情况发生了巨大变化,只需要1955.1毫秒从阵列中删除项目耗时83.6毫秒将它们从集合中删除,速度提高了23倍。

结论

在10k元素下,两个测试都运行相当的时间(数组:16.6毫秒,设置:20.7毫秒)但是当处理100k元素时,该集合是明显的赢家(数组:1974.8毫秒,设置:83.6毫秒)但仅仅因为删除操作。否则阵列更快。我不知道为什么会这样。

我玩了一些混合场景,其中创建并填充了一个数组,然后将其转换为一个集合,其中一些元素将被删除,然后该集合将被重新转换为数组。虽然这样做会比删除数组中的元素提供更好的性能,但传输到集合和从集合传输所需的额外处理时间超过了填充数组而不是集合的增益。最后,只处理一组更快。尽管如此,有一个有趣的想法是,如果选择使用数组作为一些没有重复数据的大数据的数据集合,那么如果需要在一个数据中删除许多元素,那么它可能是有利的性能。操作,将数组转换为集合,执行删除操作,并将集合转换回数组。

数组代码:

var timer = function(name) {
  var start = new Date();
  return {
    stop: function() {
      var end = new Date();
      var time = end.getTime() - start.getTime();
      console.log('Timer:', name, 'finished in', time, 'ms');
    }
  }
};

var getRandom = function(min, max) {
  return Math.random() * (max - min) + min;
};

var lastNames = ['SMITH', 'JOHNSON', 'WILLIAMS', 'JONES', 'BROWN', 'DAVIS', 'MILLER', 'WILSON', 'MOORE', 'TAYLOR', 'ANDERSON', 'THOMAS'];

var genLastName = function() {
  var index = Math.round(getRandom(0, lastNames.length - 1));
  return lastNames[index];
};

var sex = ["Male", "Female"];

var genSex = function() {
  var index = Math.round(getRandom(0, sex.length - 1));
  return sex[index];
};

var Person = function() {
  this.name = genLastName();
  this.age = Math.round(getRandom(0, 100))
  this.sex = "Male"
};

var genPersons = function() {
  for (var i = 0; i < 100000; i++)
    personArray.push(new Person());
};

var changeSex = function() {
  for (var i = 0; i < personArray.length; i++) {
    personArray[i].sex = genSex();
  }
};

var deleteMale = function() {
  for (var i = 0; i < personArray.length; i++) {
    if (personArray[i].sex === "Male") {
      personArray.splice(i, 1)
      i--
    }
  }
};

var t = timer("Array");

var personArray = [];

genPersons();

changeSex();

deleteMale();

t.stop();

console.log("Done! There are " + personArray.length + " persons.")

设置代码:

var timer = function(name) {
    var start = new Date();
    return {
        stop: function() {
            var end  = new Date();
            var time = end.getTime() - start.getTime();
            console.log('Timer:', name, 'finished in', time, 'ms');
        }
    }
};

var getRandom = function (min, max) {
  return Math.random() * (max - min) + min;
};

var lastNames = ['SMITH','JOHNSON','WILLIAMS','JONES','BROWN','DAVIS','MILLER','WILSON','MOORE','TAYLOR','ANDERSON','THOMAS'];

var genLastName = function() {
    var index = Math.round(getRandom(0, lastNames.length - 1));
    return lastNames[index];
};

var sex = ["Male", "Female"];

var genSex = function() {
    var index = Math.round(getRandom(0, sex.length - 1));
    return sex[index];
};

var Person = function() {
	this.name = genLastName();
	this.age = Math.round(getRandom(0,100))
	this.sex = "Male"
};

var genPersons = function() {
for (var i = 0; i < 100000; i++)
	personSet.add(new Person());
};

var changeSex = function() {
	for (var key of personSet) {
		key.sex = genSex();
	}
};

var deleteMale = function() {
	for (var key of personSet) {
		if (key.sex === "Male") {
			personSet.delete(key)
		}
	}
};

var t = timer("Set");

var personSet = new Set();

genPersons();

changeSex();

deleteMale();

t.stop();

console.log("Done! There are " + personSet.size + " persons.")

答案 1 :(得分:43)

  

<强>观测

     
      
  • 设置操作可以理解为执行流中的快照。
  •   
  • 我们不是一个明确的替代品。
  •   
  • Set类的元素没有可访问的索引。
  •   
  • 设置类是一个数组类补充,在我们需要存储要应用基本添加的集合的场景中非常有用,   删除,检查和迭代操作。
  •   

我分享了一些性能测试。尝试打开控制台并复制下面的代码。

创建阵列(125000)

var n = 125000;
var arr = Array.apply( null, Array( n ) ).map( ( x, i ) => i );
console.info( arr.length ); // 125000

<强> 1。查找索引

我们比较了Set with Array的has方法indexOf:

  

数组/ indexOf (0.281ms)|设置/ (0.053ms)

// Helpers
var checkArr = ( arr, item ) => arr.indexOf( item ) !== -1;
var checkSet = ( set, item ) => set.has( item );

// Vars
var set, result;

console.time( 'timeTest' );
result = checkArr( arr, 123123 );
console.timeEnd( 'timeTest' );

set = new Set( arr );

console.time( 'timeTest' );
checkSet( set, 123123 );
console.timeEnd( 'timeTest' );

<强> 2。添加新元素

我们分别比较Set和Array对象的add和push方法:

  

阵列/ 推送(1.612ms)|设置/ 添加(0.006ms)

console.time( 'timeTest' );
arr.push( n + 1 );
console.timeEnd( 'timeTest' );

set = new Set( arr );

console.time( 'timeTest' );
set.add( n + 1 );
console.timeEnd( 'timeTest' );

console.info( arr.length ); // 125001
console.info( set.size ); // 125001

第3。删除元素

删除元素时,我们必须记住,Array和Set不会在相同条件下启动。 Array没有本机方法,因此需要外部函数。

  

数组/ deleteFromArr (0.356ms)|设置/ 删除(0.019ms)

var deleteFromArr = ( arr, item ) => {
    var i = arr.indexOf( item );
    i !== -1 && arr.splice( i, 1 );
};

console.time( 'timeTest' );
deleteFromArr( arr, 123123 );
console.timeEnd( 'timeTest' );

set = new Set( arr );

console.time( 'timeTest' );
set.delete( 123123 );
console.timeEnd( 'timeTest' );

阅读完整文章here

答案 2 :(得分:3)

我的观察是,对于大型数组而言,Set总是更好,有两个陷阱:

a)从数组创建集合必须在具有预先长度的for循环中完成。

慢(例如18ms) new Set(largeArray)

快(例如6ms)  const SET = new Set(); const L = largeArray.length; for(var i = 0; i<L; i++) { SET.add(largeArray[i]) }

b)迭代可以用同样的方式完成,因为它也比for of循环快......

请参阅https://jsfiddle.net/0j2gkae7/5/

与现实生活进行比较 具有40.000个元素的difference()intersection()union()uniq()(+他们的iteratee伴侣等)

答案 3 :(得分:2)

只是属性查找,很少或零写入

如果属性查找是您的主要关注点,这里有一些数字。

JSBench 测试 https://jsbench.me/3pkjlwzhbr/1

// https://jsbench.me/3pkjlwzhbr/1
// https://docs.google.com/spreadsheets/d/1WucECh5uHlKGCCGYvEKn6ORrQ_9RS6BubO208nXkozk/edit?usp=sharing
// JSBench forked from https://jsbench.me/irkhdxnoqa/2

var theArr = Array.from({ length: 10000 }, (_, el) => el)
var theSet = new Set(theArr)
var theObject = Object.assign({}, ...theArr.map(num => ({ [num]: true })))
var theMap = new Map(theArr.map(num => [num, true]))

var theTarget = 9000


// Array

function isTargetThereFor(arr, target) {
  const len = arr.length
  for (let i = 0; i < len; i++) {
    if (arr[i] === target) {
      return true
    }
  }
  return false
}
function isTargetThereForReverse(arr, target) {
  const len = arr.length
  for (let i = len; i > 0; i--) {
    if (arr[i] === target) {
      return true
    }
  }
  return false
}

function isTargetThereIncludes(arr, target) {
  return arr.includes(target)
}

// Set

function isTargetThereSet(numberSet, target) {
  return numberSet.has(target)
}

// Object 

function isTargetThereHasOwnProperty(obj, target) {
  return obj.hasOwnProperty(target)
}
function isTargetThereIn(obj, target) {
  return target in obj
}
function isTargetThereSelectKey(obj, target) {
  return obj[target]
}

// Map

function isTargetThereMap(numberMap, target) {
  return numberMap.has(target)
}

大批
  • for 循环
  • for 循环(反向)
  • array.includes(target)
  • set.has(target)
目的
  • obj.hasOwnProperty(target)
  • target in obj <- 慢 1.29%
  • obj[target] <- 最快
地图
  • map.has(target) <- 慢 2.94%
结果来自 2021 年 1 月,Chrome 87

enter image description here

欢迎来自其他浏览器的结果,请更新此答案。
您可以使用 this spreadsheet 制作精美的屏幕截图。

JSBench 测试分叉自 Zargold's answer.

答案 4 :(得分:1)

我最近运行了这个测试,发现Set的表现远远超过1000个项目的数组(大约10倍的操作可能在同一时间段内发生)。并且取决于浏览器要么像对象那样击败或丢失到Object.hasOwnProperty。

Set和Object都有&#34; has&#34;方法执行似乎分摊到O(1),但根据浏览器的实现,单个操作可能需要更长或更快。

https://jsperf.com/set-has-vs-object-hasownproperty-vs-array-includes/1 如果你想用不同的浏览器/环境运行你自己的测试。

答案 5 :(得分:0)

在本文中,您将通过示例 JavaScript search optimization 了解差异及其对性能的影响,其中一些示例解释了在搜索过程中使用 set.has() 时性能如何变化。

  1. 10 users in DB and 2 online-users
  2. 100k users in DB and 1k online users

答案 6 :(得分:-6)

console.time("set")
var s = new Set()
for(var i = 0; i < 10000; i++)
  s.add(Math.random())
s.forEach(function(e){
  s.delete(e)
})
console.timeEnd("set")
console.time("array")
var s = new Array()
for(var i = 0; i < 10000; i++)
  s.push(Math.random())
s.forEach(function(e,i){
  s.splice(i)
})
console.timeEnd("array")

对10K物品的这三项操作给了我:

set: 7.787ms
array: 2.388ms