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

来源:互联网 发布:苹果手机壁纸软件排行 编辑:程序博客网 时间:2024/05/18 02:24

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

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


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

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 < TList< TasList( 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对象


0 0
原创粉丝点击