通过Java代码技巧改善性能

前言

程序的性能受到代码质量的直接影响。这次主要介绍一些代码编写的小技巧和惯例。虽然看起来有些是微不足道的编程技巧,却可能为系统性能带来成倍的提升,因此还是值得关注的。

慎用异常

在Java开发中,经常使用try-catch进行错误捕获,但是try-catch语句对系统性能而言是非常糟糕的。虽然一次try-catch中,无法察觉到她对性能带来的损失,但是一旦try-catch语句被应用于循环或是遍历体内,就会给系统性能带来极大的伤害。

以下是一段将try-catch应用于循环体内的示例代码:

@Test
  public void test11() {

    long start = System.currentTimeMillis();
    int a = 0;
    for(int i=0;i<1000000000;i++){
      try {
        a++;
      }catch (Exception e){
        e.printStackTrace();
      }
    }
    long useTime = System.currentTimeMillis()-start;
    System.out.println("useTime:"+useTime);

  }

上面这段代码运行结果是:

useTime:10

下面是一段将try-catch移到循环体外的代码,那么性能就提升了将近一半。如下:

@Test
  public void test(){
    long start = System.currentTimeMillis();
    int a = 0;
    try {
      for (int i=0;i<1000000000;i++){
        a++;
      }
    }catch (Exception e){
      e.printStackTrace();
    }
    long useTime = System.currentTimeMillis()-start;
    System.out.println(useTime);
  }

运行结果:

useTime:6

使用局部变量

调用方法时传递的参数以及在调用中创建的临时变量都保存在栈(Stack)中,速度快。其他变量,如静态变量、实例变量等,都在堆(Heap)中创建,速度较慢。

下面是一段使用局部变量进行计算的代码:

@Test
  public void test11() {

    long start = System.currentTimeMillis();
    int a = 0;
    for(int i=0;i<1000000000;i++){
      a++;
    }
    long useTime = System.currentTimeMillis()-start;
    System.out.println("useTime:"+useTime);

  }

运行结果:

useTime:5

将局部变量替换为类的静态变量:

static int aa = 0;
  @Test
  public void test(){
    long start = System.currentTimeMillis();

    for (int i=0;i<1000000000;i++){
      aa++;
    }
    long useTime = System.currentTimeMillis()-start;
    System.out.println("useTime:"+useTime);
  }

运行结果:

useTime:94

通过上面两次的运行结果,可以看出来局部变量的访问速度远远高于类成员变量。

位运算代替乘除法

在所有的运算中,位运算是最为高效的。因此,可以尝试使用位运算代替部分算术运算,来提高系统的运行速度。最典型的就是对于整数的乘除运算优化。

下面是一段使用算术运算的代码:

@Test
  public void test11() {

    long start = System.currentTimeMillis();
    int a = 0;
    for(int i=0;i<1000000000;i++){
      a*=2;
      a/=2;
    }
    long useTime = System.currentTimeMillis()-start;
    System.out.println("useTime:"+useTime);
  }

运行结果:

useTime:1451

将循环体中的乘除运算改为等价的位运算,代码如下:

@Test
  public void test(){
    long start = System.currentTimeMillis();
    int aa = 0;
    for (int i=0;i<1000000000;i++){
      aa<<=1;
      aa>>=1;
    }
    long useTime = System.currentTimeMillis()-start;
    System.out.println("useTime:"+useTime);
  }

运行结果:

useTime:10

上两段代码执行了完全相同的功能,在每次循环中,都将整数乘以2,并除以2。但是运行结果耗时相差非常大,所以位运算的效率还是显而易见的。

提取表达式

在软件开发过程中,程序员很容易有意无意地让代码做一些“重复劳动”,在大部分情况下,由于计算机的高速运行,这些“重复劳动”并不会对性能构成太大的威胁,但若希望将系统性能发挥到极致,提取这些“重复劳动”相当有意义。

比如以下代码中进行了两次算术计算:

@Test
  public void testExpression(){
    long start = System.currentTimeMillis();
    double d = Math.random();
    double a = Math.random();
    double b = Math.random();
    double e = Math.random();

    double x,y;
    for(int i=0;i<10000000;i++){
      x = d*a*b/3*4*a;
      y = e*a*b/3*4*a;
    }
    long useTime = System.currentTimeMillis()-start;
    System.out.println("useTime:"+useTime);

  }

运行结果:

useTime:21

仔细看能发现,两个计算表达式的后半部分完全相同,这也意味着在每次循环中,相同部分的表达式被重新计算了。

那么改进一下后就变成了下面的样子:

@Test
  public void testExpression99(){
    long start = System.currentTimeMillis();
    double d = Math.random();
    double a = Math.random();
    double b = Math.random();
    double e = Math.random();

    double p,x,y;
    for(int i=0;i<10000000;i++){
      p = a*b/3*4*a;
      x = d*p;
      y = e*p;
    }
    long useTime = System.currentTimeMillis()-start;
    System.out.println("useTime:"+useTime);
  }

运行结果:

useTime:11

通过运行结果我们可以看出来具体的优化效果。

同理,如果在某循环中需要执行一个耗时操作,而在循环体内,其执行结果总是唯一的,也应该提取到循环体外。

例如下面的代码:

for(int i=0;i<100000;i++){
  x[i] = Math.PI*Math.sin(y)*i;
}

应该改进成下面的代码:

//提取复杂,固定结果的业务逻辑处理到循环体外
double p = Math.PI*Math.sin(y);
for(int i=0;i<100000;i++){
  x[i] = p*i;
}

使用arrayCopy()

数组复制是一项使用频率很高的功能,JDK中提供了一个高效的API来实现它。

/**
   * @param   src   the source array.
   * @param   srcPos  starting position in the source array.
   * @param   dest   the destination array.
   * @param   destPos starting position in the destination data.
   * @param   length  the number of array elements to be copied.
   * @exception IndexOutOfBoundsException if copying would cause
   *        access of data outside array bounds.
   * @exception ArrayStoreException if an element in the <code>src</code>
   *        array could not be stored into the <code>dest</code> array
   *        because of a type mismatch.
   * @exception NullPointerException if either <code>src</code> or
   *        <code>dest</code> is <code>null</code>.
   */
  public static native void arraycopy(Object src, int srcPos,
                    Object dest, int destPos,
                    int length);

如果在应用程序中需要进行数组复制,应该使用这个函数,而不是自己实现。

下面来举例:

@Test
  public void testArrayCopy(){
    int size = 100000;
    int[] array = new int[size];
    int[] arraydest = new int[size];

    for(int i=0;i<array.length;i++){
      array[i] = i;
    }
    long start = System.currentTimeMillis();
    for (int k=0;k<1000;k++){
      //进行复制
      System.arraycopy(array,0,arraydest,0,size);
    }
    long useTime = System.currentTimeMillis()-start;
    System.out.println("useTime:"+useTime);
  }

运行结果:

useTime:59

相对应地,如果在程序中,自己实现数组复制,其等价代码如下:

@Test
  public void testArrayCopy99(){
    int size = 100000;
    int[] array = new int[size];
    int[] arraydest = new int[size];

    for(int i=0;i<array.length;i++){
      array[i] = i;
    }
    long start = System.currentTimeMillis();
    for (int k=0;k<1000;k++){
      for(int i=0;i<size;i++){
        arraydest[i] = array[i];
      }
    }
    long useTime = System.currentTimeMillis()-start;
    System.out.println("useTime:"+useTime);
  }

运行结果:

useTime:102

通过运行结果可以看出效果。

因为System.arraycopy()函数是native函数,通常native函数的性能要优于普通函数。仅出于性能考虑,在程序开发时,应尽可能调用native函数。

(0)

相关推荐

  • java程序员必须知道的4个书写代码技巧

    如果现在要求对你写的Java代码进行优化,那你会怎么做呢?作者在本文介绍了可以提高系统性能以及代码可读性的四种方法,如果你对此感兴趣,就让我们一起来看看吧. 我们平时的编程任务不外乎就是将相同的技术套件应用到不同的项目中去,对于大多数情况来说,这些技术都是可以满足目标的.然而,有的项目可能需要用到一些特别的技术,因此工程师们得深入研究,去寻找那些最简单但最有效的方法.在以前一篇文章中,我们讨论了必要时可以使用的四种特殊技术,这些特殊技术可以创建更好的Java软件:而本文我们将介绍一些有助于解决常

  • 通过Java代码技巧改善性能

    前言 程序的性能受到代码质量的直接影响.这次主要介绍一些代码编写的小技巧和惯例.虽然看起来有些是微不足道的编程技巧,却可能为系统性能带来成倍的提升,因此还是值得关注的. 慎用异常 在Java开发中,经常使用try-catch进行错误捕获,但是try-catch语句对系统性能而言是非常糟糕的.虽然一次try-catch中,无法察觉到她对性能带来的损失,但是一旦try-catch语句被应用于循环或是遍历体内,就会给系统性能带来极大的伤害. 以下是一段将try-catch应用于循环体内的示例代码: @

  • 分享令人目瞪口呆的 Java 代码技巧

    技术点 本文不是一个吹嘘的文章,不会讲很多高深的架构,相反,会讲解很多基础的问题和写法问题,如果读者自认为基础问题和写法问题都是不是问题,那请忽略这篇文章,节省出时间去做一些有意义的事情. 基于 Spring Boot + MyBatis Plus + Vue & Element 实现的后台管理系统 + 用户小程序,支持 RBAC 动态权限.多租户.数据权限.工作流.三方登录.支付.短信.商城等功能. 项目地址:https://github.com/YunaiV/ruoyi-vue-pro 开发

  • 使用Java代码获取服务器性能信息及局域网内主机名

    最近做个项目,就是要取得cpu占有率等等的系统信息,一开始以为要用动态链接库了,但后来发现可以像下面这样做,不去调用jni,这样省去了很多看新技术的时间o(∩_∩)o... 在Java中,可以获得总的物理内存.剩余的物理内存.已使用的物理内存等信息,下面例子可以取得这些信息,并且获得在Windows下的内存使用率.      首先编写一个MonitorInfoBean类,用来装载监控的一些信息,包括物理内存.剩余的物理内存.已使用的物理内存.内存使用率等字段,该类的代码如下: package c

  • kotlin改善java代码实例分析

    序 本文主要举几个kotlin如何改善java代码的例子 字符串字面值及模板 字符串字面值 @Test fun testStringLiterals(){ val a = """if(a > 1) { | return a |}""".trimMargin() println(a) val b = """Foo Bar""".trimIndent() println(b) } 有了

  • 分享几个写简洁java代码的小技巧

    目录 1.定义配置文件信息 2.用@RequiredArgsConstructor代替@Autowired 3.不要返回null 4.ifelse 5.减少controller业务代码 6.将字符串数组转换成逗号分隔字符串 总结 1. 定义配置文件信息 有时候我们为了统一管理会把一些变量放到 yml 配置文件中 例如 用 @ConfigurationProperties 代替 @Value 使用方法 定义对应字段的实体 @Data // 指定前缀 @ConfigurationProperties

  • 5个可以在Golang中优化代码以提高性能的技巧分享

    作为一名软件工程师,确保你的代码高效且性能良好是非常重要的.在Golang中,有几个最佳实践和技术可以用来优化你的代码,以获得更好的性能.这里有五个技巧可以帮助你开始工作: 1.明智地使用指针.Golang使用指针来引用内存位置.虽然指针在某些情况下很有用,但如果过度或不正确地使用,它们也会导致性能下降.例如,使用指针向函数传递大的结构或 slice 会导致不必要的内存分配和复制.相反,可以考虑通过值传递这些类型. // Bad: Passing a large slice by pointer

  • java 中锁的性能提高办法

    java 中锁的性能提高办法 我们努力为自己的产品所遇到的问题思考解决办法,但在这篇文章中我将给大家分享几种常用的技术,包括分离锁.并行数据结构.保护数据而非代码.缩小锁的作用范围,这几种技术可以使我们不使用任何工具来检测死锁. 锁不是问题的根源,锁之间的竞争才是 通常在多线程的代码中遇到性能方面的问题时,一般都会抱怨是锁的问题.毕竟锁会降低程序的运行速度和其较低的扩展性是众所周知的.因此,如果带着这种"常识"开始优化代码,其结果很有可能是在之后会出现讨人厌的并发问题. 因此,明白竞争

  • 让Java代码更高效

    本文简单介绍一下在写代码过程中用到的一些让JAVA代码更高效的技巧. 1,将一些系统资源放在池中,如数据库连接,线程等.在standalone的应用中,数据库连接池可以使用一些开源的连接池实现,如C3P0,proxool和DBCP等,在运行在容器中的应用这可以使用服务器提供的DataSource.线程池可以使用JDK本身就提供的java.util.concurrent.ExecutorService. import java.util.concurrent.Executors; import j

  • 提高PHP性能的编码技巧以及性能优化详细解析

    0.用单引号代替双引号来包含字符串,这样做会更快一些.因为PHP会在双引号包围的字符串中搜寻变量,单引号则不会,注意:只有echo能这 么做,它是 一种可以把多个字符串当作参数的"函数"(译注:PHP手册中说echo是语言结构,不是真正的函数,故把函数加上了双引号). 1.如果能将类的方法定义成static,就尽量定义成static,它的速度会提升将近4倍. 2.$row['id'] 的速度是$row[id]的7倍. 3.echo 比 print 快,并且使用echo的多重参数(译注:

随机推荐