范文健康探索娱乐情感热点
投稿投诉
热点动态
科技财经
情感日志
励志美文
娱乐时尚
游戏搞笑
探索旅游
历史星座
健康养生
美丽育儿
范文作文
教案论文
国学影视

Java中一个你不常用,但是关键时刻可以帮我们提升性能的一个知识点

  每天早上七点三十,准时推送干货
  最近阿粉在实现一个功能的时候,遇到了一个性能问题,一个方法在某些场景下运行时长达到了 4s 多,虽然说业务功能是实现了,但是不管是从业务的角度还是作为一个有追求的程序员,都是不能接受的,所以优化这个方法势在必行。在优化的过程中就用到了本文要说明的一个知识点,看阿粉慢慢道来。
  在提供优化代码之前,先简单的描述一下这个方法做的事情,要做的事情很简单,就是返回一个整数,整数表示的是二进制数组中有多少个 1。给到了入参是一个 Map其中key我们不关心,value是二进制字符串。需要注意的是二进制字符串的长度很长,10 万位左右;并且长度不一定相同。我们需要做的事情就是将所有的二进制字符串数组进行或运算,得到一个最终的二进制数组,然后计算其中 1 的个数进行返回。
  根据我们上面的分析,列一下我们写代码的步骤:
  因为我们要按位进行或运算,所以二进制的长度应该要一样才行,我们取最长的二进制的长度为 maxLength,所有没有这么长的二进制字符串,我们需要进行前面补 0 ;
  将所有的二进制字符串按位进行或运算;
  遍历最终的数组输出 1 的个数;
  按照这个思路,我们可以写出下面的代码,maxLength作为入参传递到我们的方法中。public static long version1(Map map, int maxLength) { long result = 0L; if (!CollectionUtils.isEmpty(map)) { //1. 将长度不够 maxLength 长的二进制字符串前面补 0 for (Map.Entry m : map.entrySet) { if (m.getValue.length < maxLength) { StringBuilder newValue = new StringBuilder; for (int i = 0; i < maxLength - m.getValue.length; i++) { newValue.append(0); } newValue.append(m.getValue); map.put(m.getKey, newValue.toString); } } //2. 将每个关键字的二进制字符串按位进行或 | 运算 Integer sum = new Integer[maxLength]; for (int i = 0; i < maxLength; i++) { sum[i] = 0; } for (Map.Entry m : map.entrySet) { for (int i = 0; i < maxLength; i++) { String substring = m.getValue.substring(i, i + 1); sum[i] = sum[i] | Integer.parseInt(substring); } } //3. 统计计算结果中 1 的个数 for (Integer integer : sum) { result += integer; } } return result; }
  简单分析一下:
  第一步的时候我们构造了一个 StringBuilder对象,根据二进制字符串的长度和maxLength的长度,在前面进行补 0 操作,两者相差多少就在前面补多少个 0,然后将原始的二进制补到最后,得到一个新的二进制字符串;
  第二步我们遍历 Map,将二进制字符串中的每一位与之前构造的全是 0 的sum数组进行或运算操作,并将结果写到sum数组对应的位置上,因为经过第一步的补 0 这里Map中所有的value的长度是一样的;
  第三步再遍历 sum数组,将每一位累加起来,得到的结果就是我们需要的结果,因为sum数组中只有 1 和 0,所以总和就是 1 的个数。
  代码写到这里,内心毫无波澜,没有一丝丝感觉,毕竟只要思路清晰,代码的实现都是小事。
  然而就当把这个功能发到测试环境的时候,测试妹妹反馈某些情况下在前端页面等待的时间太长了,loading的小按钮一直转不停,往往要 4,5 秒的时间才能得到结果,体验太差了。
  抱着以用户体验为目标的决心(其实是怕被扣工资),阿粉看了一下测试用例,追踪了一下代码结果发现当这个方法中 map中的 key 达到 1000+ 的时候,整个方法竟然执行了4s 多!是可忍孰不可忍,作为一个有追求的程序员怎么能让这种情况发生了,不得已阿粉走上了优化这个方法的道路。
  优化之前我们当然需要知道有哪些可以优化的地方,看下这段代码,发现里面好多 for循环,毫无疑问我们的优化目标就是降低for循环的个数以及次数。
  仔细看了一下代码,我们想一想真的有必要要先将每个二进制字符串进行前面补 0 的动作吗?是不是可以在进行或运算的时候发现位数不够的时候自动补 0 呢?还有就是我们真的有必要在最后遍历 sum数组,得到 1 的个数吗?因为是或运算,只要sum[i]是 1 了,或运算得到的结果就一定是 1 那这个时候是不是就可以得到结果呢?
  带着这两个问题,将代码优化成了下面的样子:public static long version2(Map map, int maxLength) { long result = 0L; if (!CollectionUtils.isEmpty(map)) { Integer sum = new Integer[maxLength]; for (int i = 0; i < maxLength; i++) { sum[i] = 0; } // 1. 将长度不够 maxLength 长的二进制字符串前面补 0 // 2. 并将每个关键字的二进制字符串按位进行或 | 运算 for (Map.Entry m : map.entrySet) { String value = m.getValue; for (int i = maxLength - 1; i >= 0; i--) { char c; int index = value.length - i - 1; if (index < 0) { c = "0"; } else { c = value.charAt(index); } //3. 统计计算结果中 1 的个数 int temp = sum[i]; sum[i] = sum[i] | Integer.parseInt(String.valueOf(c)); if (temp == 0 && sum[i] == 1) { result += 1; } } } } return result; }
  简单分析一下,我们可以从数组的最后一位开始进行按位或运算,这样当得到的 index小于 0 的时候,表示该二进制数组已经遍历完了,那么这个时候如果还没有达到maxLength的长度,我们就补 0,用 0 进行或运算;同时我们在进行或运算的时候,通过记录sum[i]在或运算前和或运算后差异来记录 1 的个数,我们只记录或运算前sum[i] == 0或运算后sum[i] == 1的值,就是我们需要的结果。
  经过我们优化后的代码,首先从 for循环的个数来看就已经减少了,我们测试一下效果如下,这里因为二进制的数组很长,不能放到公众号文章里面,就简化了。public static void main(String[] args) { String binaryString1 = "1000101010010101010101010100110101010101001001010101010101..."; Map map = new HashMap<>(16); for (int i = 0; i < 1500; i++) { map.put("key_" + i, binaryString1); } int maxLength = 0; for (Map.Entry m : map.entrySet) { maxLength = Math.max(maxLength, m.getValue.length); } long start1 = System.currentTimeMillis; long aLong1 = version1(map, maxLength); System.out.println("version1:" + aLong1 + ":" + (System.currentTimeMillis - start1));
  long start2 = System.currentTimeMillis; long aLong2 = version2(map, maxLength); System.out.println("version1:" + aLong2 + ":" + (System.currentTimeMillis - start2)); }
  从测试结果我们可以看到,当 map size在 1000 的时候,version1耗费了 4034ms,version2 耗费了 2090ms,性能提升接近 2 倍说明我们的优化还是有效果的。
  事情到了这里,你以为就结束了吗?那就错了,因为还没有提到阿粉前面说的知识点,下面重点来了,请注意看。version2的代码我们能不能再优化了?不管能不能再优化,有一行代码看起来总是让人很不爽,那就是sum[i] = sum[i] | Integer.parseInt(String.valueOf(c));这一行,将char字符,转换成String,再通过Integer.parseInt转成int的 0 或者 1 来进行或运算。很容易让人想到,这里经过几层的包装转换,是很浪费资源的,所以这里也是我们优化的点。
  这一行的目标是进行或运算,Integer.parseInt(String.valueOf(c))的目标就是将char的 0 或者 1 转成int的 0 或者 1。那为什么我们不直接用c?然后我们测试了一下下面的代码,结果跟我们想象的不太一样,但是这个结果也是可以用的,我们再后面减掉一个 48 是不是就可以了呢?得到的就是 0 和 1 了。
  经过上面的测试,我们 version3版本的代码如下:public static long version3(Map map, int maxLength) { long result = 0L; if (!CollectionUtils.isEmpty(map)) { Integer sum = new Integer[maxLength]; for (int i = 0; i < maxLength; i++) { sum[i] = 0; } // 1. 将长度不够 maxLength 长的二进制字符串前面补 0 // 2. 并将每个关键字的二进制字符串按位进行或 | 运算 for (Map.Entry m : map.entrySet) { String value = m.getValue; for (int i = maxLength - 1; i >= 0; i--) { char c; int index = value.length - i - 1; if (index < 0) { c = "0"; } else { c = value.charAt(index); } //3. 统计计算结果中 1 的个数 int temp = sum[i]; sum[i] = sum[i] | ((int) c - 48); if (temp == 0 && sum[i] == 1) { result += 1; } } } } return result; }
  测试结果如下:
  我们发现在同样大小的情况下,version3版本直接进入到了 1 秒了,只用了 746ms,这次的优化性能提升了接近 5.5 倍!至此此次的性能优化终于画上了句号。
  相信看到这里的小伙伴也知道了阿粉前面提到的知识点是什么了,那就是 char类型可以跟int做转换,其实这就是我们学编程之初学到的ASCII码,可能学习的时候并没有想过要怎么用,当真正用到的时候就会发现真香!
  总结一下今天阿粉给大家介绍了如果将一个运行 4s 多的方法,优化到了 800ms 以内,通过实战介绍了 ASCII 在我们日常工作中的应用。如果大家觉得看了文章的内容有收获,欢迎小伙伴们收藏,点赞,评论,转发,每一次互动都是对阿粉的鼓励。"
  文章中的源码和 ASCII 对照表,阿粉以及放在公众号后台了,回复关键字【ASCII】即可获得。
  往期精彩回顾
  你真的了解计算机病毒吗?内容很"干",记得喝水
  面试官提问:什么是动态代理?
  号外!号外!
  Java 极客技术微信群中有很多优秀的小伙伴在讨论技术,偶尔还有不定期的资料分享和红包发放!如果你想提升自己,并且想和优秀的人一起进步,请添加下方微信,阿粉会迅速拉你进群。
  注意:添加好友时,备注【加群】可以更快的拉你进群哦!
  喜欢就分享
  认同就
  支持就在看
  一键四连,你的offer也四连

财务每日知识点把杂乱的财务知识模块化大家好!我是幻化意识流,简称意识流。今天突发奇想,想把我们经常用到的财务概念模块化,便于我们的记忆。我的理念是,近几年各种财税信息更新的非常的快,让我们这些会计只有招架之功,却无还每次10分钟跟我学Python(第二次)大家好!我是幻化意识流,简称意识流。现在我们继续Python的学习。工欲善其事必先利其器!我们搭积木得需要一张桌子和积木,这个桌子就是我们的编程环境,现在我们先来获取这个桌子。我们每次10分钟跟我学Python(第四次)大家好!我是幻化意识流,简称意识流。现在我们继续Python的学习。我们打开IDLE输入11(两个整型数据相加组成的表达式)0hr0110(单个的0也是一个表达式)0hr0。112每次10分钟跟我学Python(第五次)大家好!我是幻化意识流,简称意识流。现在我们继续Python的学习。我们打开IDLE。我们对图片中的信息解释一下Python3。7。9(tagsv3。7。913c94747c7,A为什么签了三方协议电子税务局显示没签呢?我来回答!我发现自己真是个多面手,不带这样夸自己的啊!朋友笑话我说。最近朋友问我,她在电子税务局缴税时,发现没有三方协议,正好这个事情我也碰到了,于是发到这里,凡是碰到这种情况的看看这里就知每次10分钟跟我学Python(第十次课)大家好!我是幻化意识流。今天继续学Python。先做一个练习计算的优先顺序是算数比较布尔(notandor)。下面出道题,先不要看后面的答案,先自己试着编一下,相信自己哦。思路是编每次10分钟跟我学Python(第一次)大家好!我是幻化意识流,简称意识流。开立这头条号,本来是想发些会计类的的文章,可是我觉得会计类的信息实在是太枯燥乏味了,想来想去,还是发发我的第二个爱好编程。我看了最近的编程语言类DxOMark公布了iPhoneSE2相机测试得分DxOMark公布了iPhoneSE2相机测试得分其中后置摄像头评分为101分,与iPhoneXR相同。得益于A13处理器强大的算力,在没有FaceID传感器的情况下,iPhone每次10分钟跟我学Python(第二十九次课)大家好!我是幻化意识流。今天继续跟我学Python。今天我们来点有小难度的,综合运用一下以前学过的知识下面我们做一个gui版的猜数游戏,上次我们写过一个猜数游戏,找出那个代码我们修每次10分钟跟我学Python(第三十次课)大家好!新年快乐,牛年大吉!我是幻化意识流。今天继续跟我学Python。过年走亲访友,闲暇时间我觉得写头条文章还是最有意思的,当然还是写关于编程的文章最有意思。我手头这台电脑是新的物以类聚英语单词记忆卡片问题问题question英kwestnn问题v提问(对具体的问题提问)problem英prblmn。(逻辑抽象的棘手的)问题adj。找麻烦的issue英un。(重要议题国际争论的)问题
6款私藏小众APP,简直开了挂,满足你的奇怪需求Hello,大家好这次的要分享的是一批小众APP,虽然名字听起来奇奇怪怪,但功能就像开了挂一样,能满足你的奇怪需求,深受网友喜爱,好评度极高。1。手电筒手电筒手机里都有自带,但只有华为P60Pro渲染图曝光,五颗镜头5000mAh电池,6488锁定机皇许多为老用户想换手机,但其他品牌的不喜欢,但华为一直没出新机,这样许多华为老用户十分期待华为出新机,最近一些博主爆料了关于P60Pro的渲染图和配置。先说配置华为P60Pro的屏幕Driver。js开源无依赖的新手交互引导库,功能强大高度可定制可以快速实现新手引导效果,自带动画,体验优秀,而且还能高度定制。关于Driver。jsDriver。js是一个可以轻松实现新手指引交互的javascript工具库,主要的作用是为刚你想要拍照很清晰的手机,这3款新手机不要错过喜欢拍照的朋友,都希望买到一部拍照很清晰的智能手机,由于目前的手机市场中,机型太多,想要挑选到一部拍照清晰的智能手机很容易,但想要找到一款拍照很清晰的手机,还需要花费一些时间去对比买完新手机,旧手机都怎么处理?早上一个老客户,买了一台苹果13256G蓝色。他的手机确实也该换了,苹果7就是在我手里买的,现在128G已经不能满足他的需求了,电池也不耐用了。苹果13256G蓝色今天苹果1325C17在业务代码中最好用的十个特性作者jinshang,腾讯WXG后台开发工程师自从步入现代C时代开始,C语言标准形成了三年一个版本的惯例C11标志着现代C的开端,C14在11的基础上查缺补漏,并未加入许多新特性,LongChina50新闻腾讯体育大裁员,六大业务组被裁撤5月19日,腾讯控股集团发布了一则关于OVBU体育业务部组织架构调整的通知,宣布裁撤篮球运营组足球运营组综合大项目运营组市场营销中心产品中心增值产品组平台研发中心推荐平台组平台研发已有45人确诊,这类快递全部消杀封存!多地紧急提醒近日,北京房山韵达快递聚集性疫情导致多人感染。国家邮政局要求各地妥善处置涉疫快件,坚决防止疫情扩散蔓。韵达快递相关疫情已有45人感染北京社区村防控全面升级5月17日,在北京市新型冠微软因通胀给员工涨薪,为何外企加薪,国内大厂却纷纷裁员?5月17日讯,据新浪科技消息,微软CEO萨提亚纳德拉(SatyaNadella)周一告诉员工,由于劳动力市场渐趋紧张,通胀居高不下,微软决定给员工加薪。国外企业应对通胀,经济下行给爆火的猴哥说车,背后到底有没有专业运营,实锤了据2022年5月16日的新华某报第10版报道,探秘千万粉丝级账号是如何炼成的,提到小灿灿新媒体机构是南京本地最早的新媒体公司之一,孵化出猴哥说车小鱼海棠潘姥姥等爆火的短视频账号。可红米note11TPro参数配置红米note11TPro最新详细参数价格红米note11TPro这款手机是红米note11T系列最高的手机配置,可以为用户提供高颜值的同时,可以为用户提供很好的手机性能体验,那么这款手机具体的参数是什么呢?一主要参数红米