关于高性能数组去重写法

213 阅读2分钟

最近在阅读了一些关于数组去重的文章,亲自比较了一些实现数组去重的思路

生成一个 distinct_array.js 文件;文件内容如下

1.数据准备(100000)

     const testArr = (function () {
       const arr = new Array(1000000);
       for (let i = 0; i < arr.length; i += 1) {
         arr[i] = Number((Math.random() * 10).toFixed());
       }
       return arr;
     }());

     console.log(`处理的数组长度${testArr.length}`);
   

2. 打印实验运算时间函数

    function loggerDistinctTime(testArr, distinctFunction, distinctFunctionName) {
      const start = Date.now();
      const arr = distinctFunction(testArr);
      const end = Date.now();
      const takeUpTime = end - start;
      console.log(`耗时:${takeUpTime}  ${distinctFunctionName}, 去重后数组: ${arr}, 去重后数组长度${arr.length}`);
    }
  

3.实现去重思路

3.1 ES5 语法实现

3.1.1 使用双重for循环

     function distinctDoubleFor(arr) {
       for (let i = 0; i < arr.length - 1; i += 1) {
         for (let j = i + 1; j < arr.length; j += 1) {
           if (arr[i] === arr[j]) {
             arr.splice(j, 1);
             j -= 1;
           }
          }
        }
        return arr;
      }
   

3.1.2 使用filter和indexOf

     function distinctEs5FilterAndIndexOf(arr) {
       return arr.filter((item, index) => arr.indexOf(item) === index);
     }
   

3.1.3 使用for 循环和indexOf

      function distinctEs5IndexOfAndFor(arr) {
        const distinctArr = [];
        for (let i = 0; i < arr.length; i += 1) {
          if (distinctArr.indexOf(arr[i]) === -1) {
            distinctArr.push(arr[i]);
          }
        }
        return distinctArr;
      }
   

3.1.4 使用forEach循环和indexOf

     function distinctEs5IndexOfAndForEach(arr) {
        const distinctArr = [];
        arr.forEach((item) => {
          if (distinctArr.indexOf(item) === -1) {
            distinctArr.push(item);
          }
        });
        return distinctArr;
     }
  

3.1.5 使用forEach循环和every

      function distinctEs5ForEachAndEvery(arr) {
        const distinctArr = [];
        arr.forEach((item) => {
          if (distinctArr.every(distinctArrItem => distinctArrItem !== item)) {
            distinctArr.push(item);
          }
        });
        return distinctArr;
      }
   

3.1.6 使用for循环和every

      function distinctEs5forAndEvery(arr) {
        const distinctArr = [];
        for (let i = 0; i < arr.length; i += 1) {
          if (distinctArr.every(distinctArrItem => distinctArrItem !== arr[i])) {
            distinctArr.push(arr[i]);
           }
        }
        return distinctArr;
      }
   

3.1.7 使用for循环和includes

      function distinctEs5ForAndIncludes(arr) {
        const distinctArr = [];
        for (let i = 0; i < arr.length; i += 1) {
          if (!distinctArr.includes(arr[i])) {
            distinctArr.push(arr[i]);
          }
        }
        return distinctArr;
      }
   

3.1.8 使用forEach循环和includes

      function distinctEs5ForEachAndIncludes(arr) {
        const distinctArr = [];
        arr.forEach((item) => {
          if (!distinctArr.includes(item)) {
            distinctArr.push(item);
          }
        });
        return distinctArr;
      }
   

3.2 ES6 语法实现

3.2.1 使用Set (前提数组元素为number类型)

    function distinctEs6Set(arr) {
      const distinctArr = new Set(arr);
      return [...distinctArr];
    }
  

3.2.2 使用for ... of 和结合obj的属性唯一的特性

      function distinctEs6ForOfAndFor(arr) {
        const distinctArr = [];
        const distObject = {};
        for (const item of arr) {
          if (!distObject[item]) {
            distinctArr.push(item);
            distObject[item] = 'distinct';
          }
        }
        return distinctArr;
      }
  

3.3 使用先排序后去重数组的思路

3.3.1 使用sort加for循环剔除重复

3.3.3 使用冒泡排序和for循环剔除重复

以上两种本质上是三重for 循环,很傻的做法,故不展示实现

4. 执行函数得出去重时间

    // es5 语法
    loggerDistinctTime([...testArr], distinctDoubleFor, '双重循环去重');
    loggerDistinctTime([...testArr], distinctEs5FilterAndIndexOf, 'es5filter和indexOf去重');
    loggerDistinctTime([...testArr], distinctEs5IndexOfAndForEach, 'es5indexOf和forEach循环去重');
    loggerDistinctTime([...testArr], distinctEs5IndexOfAndFor, 'es5indexOf和for循环去重');
    loggerDistinctTime([...testArr], distinctEs5forAndEvery, 'es5every和for循环去重');
    loggerDistinctTime([...testArr], distinctEs5ForEachAndEvery, 'es5every和forEach循环去重');
    loggerDistinctTime([...testArr], distinctEs5ForAndIncludes, 'es5Includes和for循环去重');
    loggerDistinctTime([...testArr], distinctEs5ForEachAndIncludes, 'es5Includes和forEach循环去重');
    // es6 语法
    loggerDistinctTime([...testArr], distinctEs6ForOfAndFor, 'es6forOf和obj去重');
    loggerDistinctTime([...testArr], distinctEs6Set, 'es6Set去重');
   

如需查看该部分代码, 参考github地址: github.com/chendongfen…

5. 总结

5.1 for循环性能比forEach高,快两倍(你还会再使用forEach这个api吗)

5.2 使用obj的去重最快

5.3 for+ indexof/includes性能不相上下(为第二梯队)