JS几种数组去重方法的性能比较

172 阅读1分钟

前言

网上数组去重的方法有很多,今天我们就来比较一下哪些方法是比较高效的。如何测试呢?我们就拿26个英文字母循环一万次,十万次,一百万次生成随机的数组来分别比较一下。

生成随机数组

let arr = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z']
    let newArr = []
    for (let i = 0; i <= 1000000; i++) {
        let random = Math.floor(Math.random() * 26)
        newArr.push(arr[random])
    }
    
1. 双重for循环:很早以前的数组去重方式。
let oneArr = [...newArr]
    let start1 = Date.now()
    for (let i = 0; i < oneArr.length; i++) {
        for (let j = i + 1; j < oneArr.length; j++) {
            if (oneArr[i] === oneArr[j]) {
                oneArr.splice(j, 1)
                j--
            }
        }
    }
    let end1 = Date.now()
    console.log(`双重for循环:${end1-start1}毫秒`)
2】Set方法(ES6中新增:将循环数组转换为Set,然后再将Set转回数组。由于Set的特性是不允许出现重复元素,所以会自动去除数组中的重复值。
let twoArr = [...newArr]
    let start2 = Date.now()
    twoArr = [...new Set(twoArr)]  //这里也可以写成 twoArr = Array.from(new Set(twoArr))
    let end2 = Date.now()
    console.log(`Set方法:${end2-start2}毫秒`)
3.indexOf方法: 利用新数组indexOf(数组元素),如果返回是-1就说明新数组里没有该元素,就往新数组里面添加元素。
let threeArr = [...newArr]
    let indexArr=[]
    let start3 = Date.now()
    for (let i = 0; i < threeArr.length; i++) {
        if (indexArr.indexOf(threeArr[i]) === -1) {
            indexArr.push(threeArr[i])
        }
    }
    let end3 = Date.now()
    console.log(`indexOf方法:${end3-start3}毫秒`)
4.sort排序(es6新增):利用 sort方法进行排序和循环,如果原数组的第 i 项和新数组的 i - 1 项不一致,就push进去。
let fourArr = [...newArr]
    let start4 = Date.now()
    fourArr = fourArr.sort()
    let sortArr = []
    for (let i = 0; i < fourArr.length; i++) {
        fourArr[i] === fourArr[i - 1] ? sortArr : sortArr.push(fourArr[i])
    };
    let end4 = Date.now()
    console.log(`sort 排序 :${end4-start4}毫秒`)
5.for + object:利用对象属性名不能重复这一特点,如果对象中不存在,就可以给push进去。
let fiveArr = [...newArr]
    let start5 = Date.now()
    let obj = {}
    let objArr = []
    for (let i = 0; i < fiveArr.length; i++) {
        if (!obj[fiveArr[i]]) {
            objArr.push(fiveArr[i])
            obj[fiveArr[i]] = 1
        }
    }
    let end5 = Date.now()
    console.log(`for+object方法 :${end5-start5}毫秒`)
6.includes(ES6新增):利用includes检查新数组是否包含原数组的每一项。 如果不包含,就push进去
let sixArr = [...newArr]
    let start6 = Date.now()
    let includesArr = []
    for (let i = 0; i < sixArr.length; i++) {
        includesArr.includes(sixArr[i]) ? includesArr : includesArr.push(sixArr[i])
    }
    let end6 = Date.now()
    console.log(`includes方法 :${end6-start6}毫秒`)

7.filter(ES6新增) + indexOf:利用filter过滤,配合indexOf查找元素。
let sevenArr = [...newArr]
    let start7 = Date.now()
    let filterArr = sevenArr.filter((item, index) => sevenArr.indexOf(item) === index)
    let end7 = Date.now()
    console.log(`filter+indexOf :${end7-start7}毫秒`)
8.reduce+includes(ES6新增):reduce不仅可以用于数组的遍历加求和,还可以去重。
let eightArr = [...newArr]
    let start8 = Date.now()
    let reduceArr = []
    // prev 是上一次回调函数的返回值,而 next 就是数组中的当前元素
    eightArr.reduce((prev, next) => {
        // 如果包含,就返回原数组,不包含,就把新数据追加到数组中 
        if (reduceArr.includes(next)) {
            return reduceArr
        } else {
            return reduceArr.push(next)
        }
    }, 0)
    let end8 = Date.now()
    console.log(`reduce+includes :${end8-start8}毫秒`)
9.Map方法(ES6新增):遍历原始数组,将数组中的每一个元素作为Key存到Map中,因为Map集合中不会出现相同的Key值,因此最终得到的Map中所有的Key值就是去重后的结果
let nineArr = [...newArr]
    let start9 = Date.now()
    let mapArr = []
    let map = new Map()
    for (let i = 0; i < nineArr.length; i++) {
        if (!map.has(nineArr[i])) { //has(key):判断是否有某个键
            map.set(nineArr[i], true); //set(key, value):添加新的键/值对
            mapArr.push(nineArr[i]);
        }
    }
    let end9 = Date.now()
    console.log(`Map方法 :${end9-start9}毫秒`)

下图是测试结果:

WechatIMG1053.jpeg

WechatIMG1054.jpeg

WechatIMG1052.jpeg

结论

从图上比较不难看出在数据量比较小的情况下,除了双重for循环之外其他的方法效率都还可以,但处理数据较多时,对象去重的方法就明显高效很多,当然Set和Map方法这两种方法也是不错的选择。