zoukankan      html  css  js  c++  java
  • 【转】JavaScript 高性能数组去重

    原文地址:https://www.cnblogs.com/wisewrong/p/9642264.html

    一、测试模版

    数组去重是一个老生常谈的问题,网上流传着有各种各样的解法

    为了测试这些解法的性能,我写了一个测试模版,用来计算数组去重的耗时

    复制代码
    // distinct.js
    
    let arr1 = Array.from(new Array(100000), (x, index)=>{
        return index
    })
    
    let arr2 = Array.from(new Array(50000), (x, index)=>{
        return index+index
    })
    
    let start = new Date().getTime()
    console.log('开始数组去重')
    
    function distinct(a, b) {
        // 数组去重
    }
    
    console.log('去重后的长度', distinct(arr1, arr2).length)
    
    let end = new Date().getTime()
    console.log('耗时', end - start)
    复制代码

    这里分别创建了两个长度为 10W 和 5W 的数组

    然后通过 distinct() 方法合并两个数组,并去掉其中的重复项

    数据量不大也不小,但已经能说明一些问题了

    二、Array.filter() + indexOf

    这个方法的思路是,将两个数组拼接为一个数组,然后使用 ES6 中的 Array.filter() 遍历数组,并结合 indexOf 来排除重复项

    function distinct(a, b) {
        let arr = a.concat(b);
        return arr.filter((item, index)=> {
            return arr.indexOf(item) === index
        })
    }

    这就是我被吐槽的那个数组去重方法,看起来非常简洁,但实际性能。。。

    是的,现实就是这么残酷,处理一个长度为 15W 的数组都需要 8427ms

    三、双重 for 循环

    最容易理解的方法,外层循环遍历元素,内层循环检查是否重复

    当有重复值的时候,可以使用 push(),也可以使用 splice()

    复制代码
    function distinct(a, b) {
        let arr = a.concat(b);
        for (let i=0, len=arr.length; i<len; i++) {
            for (let j=i+1; j<len; j++) {
                if (arr[i] == arr[j]) {
                    arr.splice(j, 1);
                    // splice 会改变数组长度,所以要将数组长度 len 和下标 j 减一
                    len--;
                    j--;
                }
            }
        }
        return arr
    }
    复制代码

    但这种方法占用的内存较高,效率也是最低的

     

    四、for...of + includes()

    双重for循环的升级版,外层用 for...of 语句替换 for 循环,把内层循环改为 includes()

    先创建一个空数组,当 includes() 返回 false 的时候,就将该元素 push 到空数组中 

    类似的,还可以用 indexOf() 来替代 includes()

    复制代码
    function distinct(a, b) {
        let arr = a.concat(b)
        let result = []
        for (let i of arr) {
            !result.includes(i) && result.push(i)
        }
        return result
    }
    复制代码

    这种方法和 filter + indexOf 挺类似

    只是把 filter() 的内部逻辑用 for 循环实现出来,再把 indexOf 换为 includes

    所以时长上也比较接近

     

      

    五、Array.sort()

    首先使用 sort() 将数组进行排序

    然后比较相邻元素是否相等,从而排除重复项

    复制代码
    function distinct(a, b) {
        let arr = a.concat(b)
        arr = arr.sort()
        let result = [arr[0]]
    
        for (let i=1, len=arr.length; i<len; i++) {
            arr[i] !== arr[i-1] && result.push(arr[i])
        }
        return result
    }
    复制代码

    这种方法只做了一次排序和一次循环,所以效率会比上面的方法都要高

     

      

    六、new Set()

    ES6 新增了 Set 这一数据结构,类似于数组,但 Set 的成员具有唯一性

    基于这一特性,就非常适合用来做数组去重了

    function distinct(a, b) {
        return Array.from(new Set([...a, ...b]))
    }

    那使用 Set 又需要多久时间来处理 15W 的数据呢?

    喵喵喵??? 57ms ??我没眼花吧??

    然后我在两个数组长度后面分别加了一个0,在 150W 的数据量之下...

    居然有如此高性能且简洁的数组去重办法?!

    七、for...of + Object

    这个方法我只在一些文章里见过,实际工作中倒没怎么用

    首先创建一个空对象,然后用 for 循环遍历

    利用对象的属性不会重复这一特性,校验数组元素是否重复

    复制代码
    function distinct(a, b) {
        let arr = a.concat(b)
        let result = []
        let obj = {}
    
        for (let i of arr) {
            if (!obj[i]) {
                result.push(i)
                obj[i] = 1
            }
        }
    
        return result
    }
    复制代码

    当我看到这个方法的处理时长,我又傻眼了

    15W 的数据居然只要 16ms ??? 比 Set() 还快???

    然后我又试了试 150W 的数据量...

    emmmmmmm.... 惹不起惹不起...

  • 相关阅读:
    winform+c#之窗体之间的传值 Virus
    ASP.NET 2.0 利用 checkbox获得选中行的行号, 在footer中显示 Virus
    .NET中的winform的listview控件 Virus
    我的书橱
    Expert .NET 2.0 IL Assembler·译者序一 写在一稿完成之即
    Verbal Description of Custom Attribute Value
    AddressOfCallBacks in TLS
    下一阶段Schedule
    2008 Oct MVP OpenDay 第二天 博客园聚会
    2008 Oct MVP OpenDay 第二天 颁奖·讲座·晚会
  • 原文地址:https://www.cnblogs.com/PeunZhang/p/11436166.html
Copyright © 2011-2022 走看看