读改善java程序的151个建议(6)

在高性能需求的Java程序中,推荐使用数组代替集合,以获得更好的性能表现。
摘要由CSDN通过智能技术生成

60.性能考虑,数组是首选

在性能要求比较高的场景中考虑使用数组替代集合。


61.若有必要,使用变长数组
java中的数组是定义的,一旦经过初始化声明就不可改变长度,这在实际使用中非常不方便。
事实上,可以通过对数组扩容“婉转”地解决该问题
例如:
public   static  <  T T []  expandCapacity (  T []  datas ,  int   newLen  ){
                newLen =  newLen <0?0:  newLen ;
                return   Arrays  .copyOf(  datas newLen );
       }

62.警惕数组的浅拷贝
这里说的是数组中的元素没有实现Serializable接口,但是通过copyOf方法产生的数组是一个浅拷贝,这与序列化的浅拷贝完全相同。
也就是:基本类型是直接拷贝值,其他都是拷贝引用地址。需要说明的是,数组的clone方法也是与此相同的,同样是浅拷贝,而且集合的clone方法也都是浅拷贝,需要在拷贝时多留心了

63.在明确的场景下,为集合指定初始容量
这里首先说的是针对ArrayList,在使用的时候,如果场景确定,比如一个班的学生,一般也都是50个左右。在初始化时,可直接指定ArrayList的长度为50*1.5=75,为什么是1.5?查看ArrayList的add方法中的 ensureCapacity方法,源码如下:
public   void  ensureCapacity  (  int   paramInt ) {
                this  .modCount += 1;
                int  i =  this  .elementData. length;
                if  (  paramInt  <= i)
                       return  ;
                Object  [] arrayOfObject =  this .elementData ;
                int  j = i * 3 / 2 + 1;
                if  (j <  paramInt )
                     j =  paramInt ;
                this  .elementData =  Arrays  .copyOf(  this . elementData, j);
       }
源码中扩容1.5倍+1,这种方式最极端的情况也就是浪费1/3,比2.5或3.5位扩容浪费少。
源码中elementData的默认长度是10,也就是说,在我们不指定ArrayList容量的情况下,默认声明的ArrayList容量是10
Vector扩容与ArrayList有点不同:源码如下:
private   void  ensureCapacityHelper  (  int   paramInt ) {
                int  i =  this  .elementData. length;
                if  (  paramInt  <= i)
                       return  ;
                Object  [] arrayOfObject =  this .elementData ;
                int  j = ( this  .capacityIncrement > 0) ? i +  this  .capacityIncrement
                           : i * 2;
                if  (j <  paramInt )
                     j =  paramInt ;
                this  .elementData =  Arrays  .copyOf(  this . elementData, j);
       }
Vector提供了一个递增步长(capacityIncrement变量),不设此值,则是容量翻倍,设置了的话,则是每次要增加的长度。
其他集合,如HashMap是按照倍数增加的,Stack继随自Vector,所以采用的是与其相同的扩容原则。

64.多种最值算法,适时选择
这里主要针对求集合的一些最值,例如最大值,最小值,第二最大值等类似的情况下。
虽然我们可以自己写冒泡算法,或是将集俣先sort一下,再直接取出来,但还可以利用集合之间的转换来实现
例如:取出一个班组的第二高分:

public   static  int   getSecond (  Integer []  data ){
                List <  Integer > dataList=  Arrays  .asList(  data );
                TreeSet  < Integer  > ts=  new   TreeSet <  Integer >(dataList);
                return  ts. lower  (ts. last  ());
              
       }

//test

Integer  [] testArr= new  Integer []{22,45,12,122,542,542,345,22};
                int  second=getSecond(testArr);
                System  .out. println  (second);

由于数组不能直接剔除重复数据,但Set集合却是可以的,而且Set的子类TreeSet还能自动排序,所以通过上面的getSecond方法,即可得出结果。
总之一句话:最值计算时使用集合最简单,使用数组性能最优。

65.避开基本类型数组转换列表陷阱
这里主要说的就是Arrays.asList方法,需要特别注意,原始类型数组不能做为asList的输入参数,否则会引起程序逻辑混乱。
Arrays.asList的方法说明:输入一个变长参数,返回一个固定长度的列表。
源码如下:
public   static  <  T List <  T asList (  T []  paramArrayOfT  ) {
                return   new  ArrayList  ( paramArrayOfT  );
       }
由于基本类型是不能泛型化的,也就是说8个基本类型不能作为泛型参数,要想作为泛型参数就必须使用其所对应的包装类型,那下面的例子:
int  data[]={1,2,3,4,5};
                List  list2= Arrays  .asList (data);
                System  .out. println  ( "列表中元素数量是:"  +list2.size());  //结果是1
                System  .out. println  ( "元素类型是:"  +list2.get(0).getClass());  //class [I
为什么没有编译报错?
在java中,数组是一个对象,它是可以泛型化的,也就是说我们的例子是把一个int类型的数组作为了T的类型,所以转换后在List中就只有一个类型为int数组的元素了。
之所以打印出"class [I" 是因为Array是属于java.lang.reflect包的,它是通过反射访问数组元素的工具类,在java中任何一个数组的类都是"[I",究其原因是就是Java并没有定义数组这一个类,它是在编译的时候生成的,是一个特殊的类。
所以只需将例子中的数组修正为包装类型即可。

66.asList方法产生的List对象不可更改
这是因为Arrays.asList中生成的ArrayList类(源码见上面65),并非java.utils.ArrayList,而是java.utils.Arrays工具类的一个内置类。也没有提供add方法。
所以除非你的list只用于只读,否则需要特别注意通过Arrays.asList生成的list对象


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值