小伙伴因 unshift 插入数据被批,未曾想到找我诉苦竟梅开二度

news2024/11/27 12:57:28

背景

事情是这样的,今天小伙伴跟我诉苦,说写的代码被批了,原因是效率太低了,简单问了一下需求,就是将几千条数据倒序插入到数组中,他是通过循环搭配 unshift 实现的,听完我也批了他一顿。

小伙伴说:你行你上啊。

我:可以通过 push 添加,所有数据添加完之后来一手 reverse ,不就是正序了?而且效率高很多。

小伙伴:凭什么这样效率更高?理论是要数据作为支撑的,show me the code!

行,那我就让他心服口服。

知其所以然

要想知道这两种方案谁的效率高,我们首先要知道它们具体是怎么实现的,那么如果让你实现 pushunshiftreverse,你会怎么去实现呢?

Array.prototype._push

知己知彼,百战不殆,要想实现 push ,首先要知道 push 做了什么。

分析

我们回忆一下 push 的细节:

  • 作用向数组末尾添加元素,可以同时添加多个,用逗号隔开。* 返回值返回数组的新的长度。### 实现
Array.prototype._push = function (...items) {for (let i = 0; i < items.length; ++i) {this[this.length] = items[i];}return this.length;
}

let arr = [1, 2, 3];
arr._push(4, 5);
console.log(arr); // 1,2,3,4,5 

一般来说,这里的 this 指向的就是调用该方法的数组,所以我们能够通过 this.length 获取数组的长度。有的小伙伴可能会好奇,this.length 的值不是一直没更新吗,那 for 循环里赋值的不一直都是同一个位置吗?

我们来看看规范里怎么说的:

The “length” property of an Array instance is a data property whose value is always numerically greater than the name of every configurable own property whose name is an array index. —— sec-properties-of-array-instances-length | ECMAScript® 2023 Language Specification (tc39.es)

简单点说就是: length 返回或设置一个数组中的元素个数,且 length 总是大于数组最高项的下标

那我们可不可以这么理解,每当我们试图做一些操作使得数组的长度增加时,数组的 length 属性都会 自动更新 ,且更新的索引为最新一次插入时的索引 + 1。

push 是通过 索引赋值 实现的,效率是 O(1)

Array.prototype._unshift

老规矩,我们首先看看 unshift 做了什么。

分析

我们回忆一下 unshift 的细节:

  • 作用将一个或多个元素添加到数组的开头,用逗号隔开。* 返回值返回数组的新的长度。### 实现
let arr = [1, 2, 3];
Array.prototype._unshift = function (...items) {const lens = items.length;for (let i = this.length - 1; i >= 0; i--) {this[i + lens] = this[i];}for (let i = 0; i < lens; ++i) {this[i] = items[i];}return this.length;
}
arr._unshift(4);
console.log(arr); // 4,1,2,3 

unshift 是通过 数组后移 让位,新增元素从头开始赋值实现的。

第一个循环从最后一个位置开始,所有元素都向后移动 lens = items.length个位置,为后续新增的元素让出位置。

有的小伙伴可能不知道为什么是让出 lens 个位置,因为我们需要向头部插入 lens 个新元素,为了让数据之间不被覆盖,前面必然要空出 lens 个位置。

而第二个循环就是为了将新元素一个个 按顺序 从索引 0 的位置开始赋值(插入)。

我们可以发现,通过 unshift 进行头部插入,每调用一次该方法,所有元素都要执行一次后移操作,这效率比通过索引赋值的时间复杂度 O(1) 不知道慢了多少。

Array.prototype._reverse

看看 reverse 做了什么。

分析

我们回忆一下 reverse 的细节:

  • 作用将数组翻转,会修改原数组。* 返回值返回翻转后的数组。### 实现
let arr = [1, 2, 3, 4];
Array.prototype._reverse = function () {const lens = Math.floor(this.length / 2);for (let i = 0; i < lens; ++i) {[this[i], this[this.length - i - 1]] = [this[this.length - i - 1], this[i]];}return this;
}
arr._reverse();
console.log(arr); // 4,3,2,1 

这里通过 lens = Math.floor(this.length / 2) 获取数组一半的长度,然后遍历 前半部分 实现翻转。

可能有的小伙伴不懂啊,我将整个数组翻转,为啥只要遍历一半?

我们可以换个角度思考:想要将数组翻转,我们只需要将第一个索引位的元素和最后一个索引位的元素进行交换,将第二个索引位上的元素和倒数第二个索引位上的二元素进行交换,以此类推,实际上只要处理到 len 索引位,是不是就已经完成了数组的整体翻转了?

还有的小伙伴对交换的这段代码感兴趣: [a, b] = [b, a],这是 ES6 新增语法,可以很简洁的实现对两个值交换。当然我们使用临时变量、异或、加减的方法实现一样可以的,大家根据个人习惯即可。

这里额外提一下三个 交换值 的方法,已经懂的小伙伴可以跳过:

异或

let a = 1, b = 12;
a ^= b;
b ^= a;
a ^= b;
console.log(a, b); // 121 

临时变量

let a = 1, b = 12;
let temp = b;
b = a;
a = temp;
console.log(a, b); // 121 

加减

let a = 1, b = 12;
a += b;
b = a - b;
a = a - b;
console.log(a, b); // 121 

数据不会骗人

即使是手动实现了这几个方法,在没有看到数据前,小伙伴仍持怀疑态度,我们话不多说,上数据。

注:所有数据都是 多次运行取均值 得出的。

插入100条数据

console.time();
let arr = [];
for (let i = 0; i < 100; ++i) {arr.push(i);
}
arr.reverse();
console.timeEnd(); // 0.093ms 
console.time();
let arr = [];
for (let i = 0; i < 100; ++i) {arr.unshift(i);
}
console.timeEnd(); // 0.094ms 

可以发现数据量小的时候差不多嘛,用哪个都问题不大。

插入1000条数据

console.time();
let arr = [];
for (let i = 0; i < 1000; ++i) {arr.push(i);
}
arr.reverse();
console.timeEnd(); // 0.127ms 
console.time();
let arr = [];
for (let i = 0; i < 1000; ++i) {arr.unshift(i);
}
console.timeEnd(); // 0.389ms 

当数据量稍微大了一点的时候,就有点微妙了。

插入10000条数据

console.time();
let arr = [];
for (let i = 0; i < 1000; ++i) {arr.push(i);
}
arr.reverse();
console.timeEnd(); // 0.553ms 
console.time();
let arr = [];
for (let i = 0; i < 10000; ++i) {arr.unshift(i);
}
console.timeEnd(); // 9.784ms 

测试发现,插入数据越多,效率差距越明显。

因为通过 unshift 需要后移数组中所有元素,耗费太多时间。而 push 通过索引添加,最后一次 reverse 也只要遍历 lens 次,效率快的多。

源码

这三个 API 在 ECMAScript 规范中的实现实际上会更复杂,因为它会对数据边界、类数组做一些容错处理,感兴趣的小伙伴点击下方链接查看。

  • reverse
  • push
  • unshift

实际案例

我们来看看一个使用 push + reverse 代替 unshift 的实际案例。

这是 big.js 里的一段源码,注意看注释:reverse faster than unshifts,底下的代码逻辑也是使用 reverse + push 的方式去实现首部插入的。

这是 big.js 的周下载量。

结束语

通过我的一番论证,小伙伴最终也是虚心接受了,程序员嘛,很单纯的,数据摆在面前胜过千言万语,我们在输出知识的同时要给出数据支撑,才能让别人心服口服。

本文通过特定场景下两种不同的插入方式,带大家了解了 unshift 可能带来的效率问题。同时希望大家不要停留于本文所讲,而是在面对问题时可以发散思维,寻找不一样的解决方案。难题没有万能解,只有因地制宜能得到最优解。

最后

为大家准备了一个前端资料包。包含54本,2.57G的前端相关电子书,《前端面试宝典(附答案和解析)》,难点、重点知识视频教程(全套)。



有需要的小伙伴,可以点击下方卡片领取,无偿分享

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/85874.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

ImageNet

标题有点不太对是的 就能用了 这次是说 用有噪声的学生网络进行自我训练提高ImageNet分类 近年来&#xff0c;深度学习在图像识别方面取得了显著的成功。然而&#xff0c;最先进的视觉模型仍然是用监督学习来训练的&#xff0c;这就需要大量的标记图像才能很好地工作。 通过只…

监控易火星版纳管IPv6:IP地址管理V2.0的进化之路

IPv6的使用&#xff0c;不仅能解决网络地址资源数量的问题&#xff0c;而且也解决了多种接入设备连入互联网的障碍。大量IP地址的接入&#xff0c;导致运维人员不得不投入大量精力来解决IP管理分散、非法接入、IP地址错误、IP地址冲突等导致的问题。随之而来的&#xff0c;IP地…

java计算机毕业设计基于安卓Android的校园助手APP

项目介绍 网络的广泛应用给生活带来了十分的便利。所以把校园助手与现在网络相结合,利用java技术建设校园助手APP,实现校园助手的信息化。则对于进一步提高校园助手发展,丰富校园助手经验能起到不少的促进作用。 校园助手APP能够通过互联网得到广泛的、全面的宣传,让尽可能多的…

Metal每日分享,图像单色滤镜效果

本案例的目的是理解如何用Metal实现图像单色效果滤镜&#xff0c;将图像转换为单色版本&#xff0c;根据每个像素的亮度进行着色&#xff1b; Demo HarbethDemo地址 实操代码 // 去雾效果滤镜 let filter C7Monochrome.init(intensity: 0.83, color: .blue)// 方案1: Image…

Zookeeper[1]-Zookeeper介绍与安装以及集群环境准备

Java 是第一大编程语言和开发平台。它有助于企业降低成本、缩短开发周期、推动创新以及改善应用服务。如今全球有数百万开发人员运行着超过 51 亿个 Java 虚拟机&#xff0c;Java 仍是企业和开发人员的首选开发平台 课程内容的介绍 Zookeeper的介绍和安装 Zookeeper客户端使用…

基于Paddle2.4.0训练报错Debug

基于Paddle2.4.0训练报错Debug一、环境简介二、Debug过程错误一Debug错误二Debug错误三Debug更换PaddlePaddle版本三、总结参考文档一、环境简介 本文背景为使用AutoDL网站的租借显卡进行网络训练&#xff0c;训练环境如下&#xff1a; 操作系统&#xff1a;Ubuntu18.04&#…

Springboot内置的工具类之StringUtils

在实际的业务开发中&#xff0c;除了经常有针对对象的判断或操作以外&#xff0c;经常也会遇到的就是字符串的判断和操作。比如判断字符串是否为空、是否以某个字符结尾、去除头部和尾部的空白字符、字符的查找和替换。在Spring的核心包中存在这样一个类org.springframework.ut…

如何保证项目如期上线,测试工程师应该怎么做?

要保证项目按照正常进度发布&#xff0c;需要整个研发团队齐心协力。 有很多原因都可能会造成项目延期。1、产品经理频繁修改需求2、开发团队存在技术难题3、测试团队测不完今天我想跟大家聊一下&#xff0c;测试团队如何保证项目按期上线&#xff0c;以及在这个过程中可能遇到…

[附源码]Python计算机毕业设计SSM基于Web课堂签到管理系统(程序+LW)

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

学生HTML网页作业:基于HTML+CSS+JavaScript画家企业8页

&#x1f389;精彩专栏推荐 &#x1f4ad;文末获取联系 ✍️ 作者简介: 一个热爱把逻辑思维转变为代码的技术博主 &#x1f482; 作者主页: 【主页——&#x1f680;获取更多优质源码】 &#x1f393; web前端期末大作业&#xff1a; 【&#x1f4da;毕设项目精品实战案例 (10…

jvm垃圾处理

1.什么是垃圾 垃圾&#xff1a;没有引用指向的一个对象或者多个对象循环引用但是没有引用指向申请内存方式 c语言&#xff1a;malloc free c:new delete java:new 对象 2、垃圾是如何产生的 垃圾一般在发生引用传递时产生。一块堆内存可以被不同的栈内存所引用&#xff0c;…

DBCO-PEG-SPDP,SPDP-PEG-DBCO,DBCO-聚乙二醇-3-(2-吡啶二硫代)丙酸N-羟基琥珀酰亚胺

一、试剂基团反应特点&#xff08;Reagent group reaction characteristics&#xff09;&#xff1a; DBCO-PEG-SPDP中无铜点击反应一直是无催化剂生物共轭的有力工具。 DBCO试剂在水性缓冲液中具有稳定性&#xff0c;可用于以高特异性和反应性标记叠氮化物修饰的生物分子&…

【知识图谱】(task3)知识图谱的存储和查询

note 用图数据库的场景&#xff1a; 高性能关系查询&#xff1a;需要快速遍历许多复杂关系的任何用例&#xff0c;如欺诈检测&#xff0c;社交网络分析&#xff0c;网络和数据库基础设施等&#xff1b;模型的灵活性&#xff1a;任何依赖于添加新数据而不会中断现有查询池的用例…

Linux Kernel 6.0 CXL Core Regs.c 详解

前言 CXL 是一个比较新的技术&#xff0c;所以我研究的内核源码是选了当前比较新的内核版本 linux 6.0。打算将内核关于 CXL 的驱动进行解析一遍&#xff0c;一步一步慢慢来。 在阅读之前&#xff0c;希望读者能有一定的 PCIe 基础知识&#xff0c;精力有限&#xff0c;不能把…

C++ 不知算法系列之聊聊希尔、归并排序算法中的分治哲学

1. 前言 排序算法中&#xff0c;冒泡、插入、选择属于相类似的排序算法&#xff0c;这类算法的共同点&#xff1a;通过不停地比较&#xff0c;再使用交换逻辑重新确定数据的位置。 希尔、归并、快速排序算法也可归为同一类&#xff0c;它们的共同点都是建立在分治思想之上。把…

Linux基本工具——gcc/g++与make/Makefile

Linux编译器&#xff0c;项目构成工具gcc/g程序翻译过程选项的含义动态链接静态链接如何识别静态链接和动态链接Linux项目自动化构建工具——make/Makefilemake/Makefile是什么make/Makefile的使用伪目标make/makefile推导过程gcc/g 程序翻译过程 预处理&#xff08;去掉注释…

当了10年程序员,我开窍了

有人说&#xff0c;程序员的高收入和工作年限成正比&#xff0c;认为自己的薪资应该如此计算&#xff1a; private static boolean 计算工资() { //years工作时长(年) int years 5; while(years-- > 0){ 做项目(); 团建活动(); 涨工资(); 拿年终奖(); } return 跳槽() &…

12、后渗透测试--meterpreter使用

Post后渗透模块&#xff1a;在meterpreter > 中我们可以使用以下的命令来实现对目标的操作。一、基本系统命令 sessions # sessions –h 查看帮助sessions -i <ID值> # 进入会话 -k 杀死会话background # 将当前会话放置后台info # 查看已有模块信息getuid …

CSS之段落样式

1、文本缩进 标签&#xff1a;text-indent &#xff08;indent v. 缩进&#xff09;含义&#xff1a;首行缩进和字体大小有关&#xff1a;1个em等于一个字体大小 2、文本对齐方式 标签&#xff1a;text-align (align v. 调整&#xff0c;使一致)种类&#xff1a;左对齐、右对…

AFDet: Anchor Free One Stage 3D Object Detection

论文链接&#xff1a;https://arxiv.org/pdf/2006.12671v1.pdf 前言 在嵌入式系统上操作的高效点云3D目标检测对于包括自动驾驶在内的许多机器人应用来说都是重要的。 大多数以前的工作都试图使用基于Anchor的检测方法来解决这个问题&#xff0c;这些方法有2个缺点&#xff1…