家人们拜托啦,请不要再写双重 for 循环了!!!

写在前面

作为一名程序员,相信大家在开发当中对于 for 循环的使用是非常非常频繁的,不知道大家有没有写过或者见别人写过类似下面的代码

for (let index = 0; index < array1.length; index++) {
  for (let index2 = 0; index2 < array2.length; index2++) {
    //.....
  }
}

这样的代码,应该挺常见的,这就是大名鼎鼎的双重 for 循环!!

不知道大家看到上面的代码什么感觉,先不说其他的,大家在工作当中会不会写类似的代码呢?

在工作当中,我们为了完成需求,一开始可能不会考虑代码质量或者优化相关的,但是如果需求完成之后是不是可以回过头来优化一下自己写的代码,这样也以防以后别人维护你的代码时候不会.....

相信大家都有自己的想法,接下来我将解释,为什么我不建议你再写双重 for 循环了!

为什么

时间复杂度

什么是时间复杂度?大家可以参考这篇文章

(算法入门)人人都能看懂的时间复杂度和空间复杂度 - 掘金 (juejin.cn)

对于双重 for 循环,其时间复杂度通常表示为 O(n^2),其中 n 是输入规模。

在双重 for 循环中,外层循环执行 n 次,内层循环对于每次外层循环执行也会执行 n 次。因此,总共的执行次数为 n * n = n^2。这就意味着随着输入规模 n 的增加,执行次数呈二次方增长。

这种二次方增长会导致算法的执行时间随着输入规模的增加而迅速增加,特别是当 n 很大时。因此,双重 for 循环可能会导致性能问题,尤其是在处理大规模数据或复杂算法时。

大概就是我们在写代码的时候尽量将时间复杂度想办法降低到 O(n),也就是一个 for 循环,而不是双重 for 循环,这有助于优化你的代码执行时间。

解决

大家可以根据我的这个小小的需求,来结合实现

对于数组 arr1,我们希望在 arr2 中与 arr1 的 id 一致的那一部分,将 arr1 的 disabled 属性设置位 true,也就是进行数据筛选。

  let arr1 = [
    {
      id: 1,
      disabled: false,
    },
    {
      id: 2,
      disabled: false,
    },
    {
      id: 3,
      disabled: false,
    },
    {
      id: 4,
      disabled: false,
    },
    {
      id: 5,
      disabled: false,
    },
    {
      id: 6,
      disabled: false,
    },
  ]
  let arr2 = [
    {
      id: 1,
    },
    {
      id: 8,
    },
    {
      id: 3,
    },
    {
      id: 9,
    },
  ]

双重for循环

使用双重 for 循环很容易解决,这个没啥说的了

  arr1.forEach((item) => {
    arr2.forEach((elemment) => {
      if (elemment.id === item.id) {
        item.disabled = true
      }
    })
  })

优化双重for循环

类似于上面的需求,我们都可以用下面的方案进行解决,很好理解

使用 Map 进行优化,还不知道 Map 咋用的我就不说了,自己看文档吧...

Map - JavaScript | MDN (mozilla.org)

第一步

将内层 for 循环所需要比较的条件存到 Map 中,使用 set 方法进行存储

  const BMap = new Map()
  arr2.forEach((item) => {
    BMap.set(item.id, true)
  })

第二步

循环外层数组,并判断是否存在在 map 中,使用 has 方法进行判断,如果存在设置相应属性即可

  arr1.forEach((item) => {
    if (BMap.has(item.id)) {
      item.disabled = true
    }
  })

在使用 has 方法查找 Map 中是否存在指定键时,时间复杂度是O(1)。

这是因为 Map 内部使用了哈希表,可以在常量时间内(即O(1))找到指定的键对应的值。

这里不建议使用 find 方法进行实现,比如:

arr1.forEach((item) => {
  const found = arr2.find(element => element.id === item.id);
  if (found) {
    item.disabled = true;
  }
});

虽然这段代码使用了 find 方法,但是在每次查找时都需要遍历 arr2 数组,因此时间复杂度可能会超过 O(n)

最终代码

  const BMap = new Map()
  arr2.forEach((item) => {
    BMap.set(item.id, true)
  })

arr1.forEach((item) => {
if (BMap.has(item.id)) {
item.disabled = true
}
})

总结

放弃使用双重 for 循环是我们每一位程序员都应该思考的,可以提高我们程序员整体的水平,对于后续的代码维护也有很大的好处,当然在业务需求紧急的情况下可以先完成需求再回过头来优化。

使用 Map 对象,提高了代码的简洁性、易读性,保证了时间复杂度维持在 O(n),所以笔者建议大家以后再遇到类似的需求,可以首先往 Map 这里想一下,而不要下意识的就直接双重for循环了。


这是一个从 https://juejin.cn/post/7368640074383065139 下的原始话题分离的讨论话题