Java可变参数的性能分析

Published: 22 Apr 2014 Category: Performace

可变长参数列表是Java 5中的一个新特性。如果方法需要传入多个同类型参数的话,这个功能就非常有用。比如说,Java 5之前如果要写一个方法来将所有入参打印到控制台上的话,它的代码会是这样的:

public static void printAll( final Object[] args )
{
    for ( int i = 0; i < args.length; ++i )
        System.out.println( args[ i ] );
}

方法调用的话会是这样:

printAll( new Object[] { new Integer( 75 ), new Date(), "String" + 50 } );

Java 5增加了对可变参数的支持。这个方法现在看起来就简单多了(译注:这里看起来简单难道不是因为新的for循环?):

public static void printAllNew( final Object... args )
{
  for ( final Object arg : args )
      System.out.println( arg );
}

它的调用也变得更简单了(由于支持了可变参数列表):

printAllNew( 75, new Date(), "String" + 50 );

最重要的一点就是,其实什么都没有发生变化。这一切其实都隐藏在背后——这些数组还是会创建的。因此如果你在代码里的内部循环中使用了可变长参数,最好确保参数列表中的所有的值都是常量。你可能经常会使用一组预定义的常量,对一个输入值进行校验。

比如说,传给你的日期串是'yyyy/mm/dd'的格式,而时间的格式是'hh:mm:ss'。如果有个方法能检查一个输入串在指定的位置只包含数字的话就会方便许多。最直接的方式当然是这么写:

public static boolean checkDigits1( final String s, final Integer... positions )
{
    for ( final Integer pos : positions )
        if ( !Character.isDigit( s.charAt( pos ) ) )
            return false;
    return true;
}

它可以这么进行调用:

checkDigits1( "2011/10/12", 0, 1, 2, 3, 5, 6, 8, 9 )

不幸的是,每次调用都会创建一个Integer数组(实际上是双倍的开销——首先会创建一个Integer数组,然后把每个int参数转化成一个Integer对象,再放到数组里)。如果显式地声明这个数组并将它传给方法的话会高效很多:

private static final Integer[] DATE_POS = { 0, 1, 2, 3, 5, 6, 8, 9 };
...
checkDigits1( "2011/10/12", DATE_POS )

还有一个问题就是我们要不要使用int来代替Integer,因为String.charAt()方法需要的是一个int参数,因此所有的Integer参数都会进行拆箱操作。但是,由于某个未知原因,在这里使用int还会更慢一些:

public static boolean checkDigits2( final String s, final int[] positions )
{
    for ( int i = 0; i < positions.length; ++i  )
        if ( !Character.isDigit( s.charAt( positions[ i ] ) ) )
            return false;
    return true;
}

新老的for循环写法的性能都是一样的。我们来对日期串"2011/10/12"进行校验,并重复一千万次:

checkDigits1varargs checkDigits1, Integer[]checkDigits2, int[]
0.304 sec0.219 sec 0.264 sec

总结

由于可变参数可以减少代码量,因此对于大多数程序来说是非常适用的,不过如果所有的参数都是已知的常量的话,最好使用预编译的数组来代替它。

原创文章转载请注明出处:Java可变参数的性能分析

英文原文链接