当前位置: 首页 > 科技观察

Java中一个你不常用到,却能在关键时刻帮助我们提升性能的知识点

时间:2023-03-16 22:14:18 科技观察

转载本文请联系Java极客科技公众号。最近阿芬在实现一个功能的时候遇到了性能问题。在某些场景下,一个方法的运行时间达到了4s以上。虽然实现了业务功能,但是无论是从业务角度还是作为追求,程序员都是无法接受的,所以优化这个方法势在必行。在优化的过程中,用到了本文要讲解的一个知识点。见阿芬缓缓解释。在给出优化后的代码之前,先简单介绍一下这个方法的作用。要做的事情很简单,就是返回一个整数,表示二进制数组中有多少个1。输入参数是一个地图。根据我们上面的分析,下面列出我们代码编写的步骤:因为我们需要逐位进行OR运算,所以二进制长度应该是一样的。我们取最长的二进制长度为maxLength,对于所有不是那么长的二进制字符串,我们需要在前面加0;对所有二进制字符串执行按位或运算;遍历最终数组输出1的个数;按照这个思路,我们可以写出如下代码,将maxLength作为输入参数传递给我们的方法。publicstaticlongversion1(Mapma??p,intmaxLength){longresult=0L;if(!CollectionUtils.isEmpty(map)){//1。在长度不超过maxLengthfor(Map.Entrym:map.entrySet()){if(m.getValue().length()m:map.entrySet()){for(inti=0;im:map.entrySet()){Stringvalue=m.getValue();for(inti=maxLength-1;i>=0;i--){charc;intindex=value.length()-i-1;if(index<0){c='0';}else{c=value.charAt(index);}//3。统计计算结果中1的个数inttemp=sum[i];sum[i]=sum[i]|Integer.parseInt(String.valueOf(c));if(temp==0&&sum[i]==1){result+=1;}}}}returnresult;}简单分析一下,我们可以从数组的最后一位开始进行按位或操作,这样当得到的索引小于0时,就表示是二进制数组了已经遍历过了,所以如果此时还没有达到maxLength的长度,我们就填0,用0进行OR运算;同时,我们在进行OR运算的时候,记录OR运算前和OR运算后的sum[i]的差值记录1的个数,我们只记录or运算前的orsum[i]==0orsum[i]手术后i]==1的值就是我们需要的结果。经过我们优化后的代码,首先减少了for循环的次数。下面我们来测试一下效果。这里,因为二进制数组很长,不能放在公众号文章中,所以进行了简化。publicstaticvoidmain(String[]args){StringbinaryString1="1000101010010101010101010100110101010101001001010101010101...";Mapma??p=newHashMap<>(16);for(inti=0;i<1500;i++){map.put("key_"+i,binaryString1);}intmaxLength=0;for(Map.Entrym:map.entrySet()){maxLength=Math.max(maxLength,m.getValue().length());}longstart1=System.currentTimeMillis();longaLong1=version1(map,maxLength);System.out.println("version1:"+aLong1+":"+(System.currentTimeMillis()-start1));longstart2=System.currentTimeMillis();longaLong2=version2(map,maxLength);System.out.println("version1:"+aLong2+":"+(System.currentTimeMillis()-start2));}从测试结果可以看出,当当地图大小为1000时,version1耗时4034ms,version2耗时2090ms。性能提升接近2倍,说明我们的优化还是有效的。你认为事情就这样结束了吗?那你就错了,因为之前阿芬提到的知识点没有提到。以下要点来了,请注意。我们可以优化version2的代码吗?不管能不能优化,总有一行代码看起来很不爽,就是sum[i]=sum[i]|Integer.parseInt(String.valueOf(c));这一行将char字符转换成String,然后通过Integer.parseInt()转换成int的0或1进行OR运算。很容易想到,这里经过几层封装转换,很浪费资源,所以这也是我们的优化点。这一行的目标是执行OR运算,Integer.parseInt(String.valueOf(c))的目标是将char的0或1转换为int的0或1。那我们为什么不直接用c呢?然后我们测试了下面的代码,结果和我们想象的不太一样,但是这个结果也是可以用的,后面减48可以吗?得到它那是0和1。经过上面的测试,我们的version3版本的代码如下:最长长度];for(inti=0;im:map.entrySet()){Stringvalue=m.getValue();for(inti=maxLength-1;i>=0;i--){charc;intindex=value.length()-i-1;if(index<0){c='0';}else{c=value.charAt(index);}//3.统计计算结果中1的个数inttemp=sum[i];sum[i]=sum[i]|((int)c-48);if(temp==0&&sum[i]==1){result+=1;}}}}returnresult;}测试结果如下:我们发现在同样大小的情况下,version3版本直接进入1秒,只用了746ms。这次优化性能提升了近5.5倍!至此,本次的性能优化终于画上了句号。相信看到这里的小伙伴也知道阿粉上面提到的知识点是什么,那就是char类型可以转int。其实这就是我们在编程之初学到的ASCII码。学习的时候可能不会用,从来没有想过怎么用,但是真正用到的时候,会发现真的很香!综上所述,今天阿芬给大家介绍了如何将一个运行时间超过4s的方法优化到800ms以下,通过实战将ASCII引入到我们的日常工作中。在应用程序中。如果觉得阅读文章内容有收获,欢迎小伙伴们收藏、点赞、评论、转发。每一次互动都是对阿凡的鼓励。