Javascript Set vs. Array performance

2022-08-30 04:28:12

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


答案 1

好的,我已经测试了从数组和集合中添加,迭代和删除元素。我运行了一个“小”测试,使用10 000个元素和一个“大”测试,使用100 000个元素。以下是结果。

向集合添加元素

数组方法似乎比 set 方法快 4 倍左右,无论添加的元素数量如何。.push.add

迭代和修改集合中的元素

对于这部分测试,我使用了一个循环来循环访问数组,并使用一个循环来循环访问集合。同样,迭代数组的速度更快。这一次,它似乎呈指数级增长,因为在“小”测试期间花费的时间是其两倍,在“大”测试期间花费的时间几乎是其四倍。forfor of

从集合中删除元素

这就是它变得有趣的地方。我使用了循环的组合,并从数组中删除了一些元素,并且我使用并从集合中删除了一些元素。对于“小”测试,从集合中删除项目的速度大约快三倍(2.6 ms vs 7.1 ms),但对于“大”测试来说,情况发生了巨大变化,从数组中删除项目需要1955.1 ms,而从集合中删除项目只需要83.6 ms,速度快了23倍。for.splicefor of.delete

结论

在 10k 个元素下,两个测试运行的时间相当(数组:16.6 ms,set:20.7 ms),但是当处理 100k 个元素时,该集合显然是赢家(数组:1974.8 ms,set:83.6 ms),但仅仅是因为删除操作。否则,阵列速度会更快。我不能确切地说为什么会这样。

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

数组代码:

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.")

答案 2

意见

  • 设置操作可以理解为执行流中的快照。
  • 我们面前不是一个最终的替代品。
  • Set 类的元素没有可访问的索引。
  • Set 类Array 类的补充,在需要存储集合以应用基本加法、删除、检查和迭代操作的方案中很有用。

我分享了一些性能测试。尝试打开主机并复制粘贴下面的代码。

创建数组 (125000)

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

1. 查找索引

我们将 have 方法的 Set 与 Array 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. 删除元素

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

阵列/删除从Arr (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' );

在此处阅读完整文章