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对象