五、编写高质量的代码—数组和集合(笔记)

本博文为《编写高质量代码—改善Java程序的151个建议》一书的阅读笔记。该书从很多方面给予了编写高质量代码的宝贵经验。而且该书应该是那种开发经验越丰富,体会越深的书籍。在阅读过程中,从该书中收获良多,这里主要作下书籍笔记,有体会的地方加点自己的想法。受限于知识水平,部分内容还没能深刻体会,所以更多更好的内容和具体实例还需要从书中去找寻。

一、性能考虑,数组是首选

在Java中数组虽然没有List、Set、Map这些集合类用起来方便,但是在基本类型处理方面,数组还是占优势的,而且集合类的底层也是通过数组实现的。所以在性能要求高的场景中推荐使用数组而不是集合。

二、若有必要,使用变长数组

Java中的数组是定长的,声明后就不可以改变长度,在使用中非常不方便。但是我们可以使用Arrays.copyOf实现数组动态扩容,这样既保证了效率,也提高了可用性。其实集合类的动态扩容也是同理。

三、警惕数组的浅拷贝

Arrays.copyOf方法产生的数组是一个浅拷贝,与序列化的浅拷贝、数组的clone方法、集合的clone方法一样都是浅拷贝:基本类型拷贝值,其他则拷贝引用。因此拷贝对象引用的值的修改也会影响被拷贝的对象的值,因为引用的是同一个地址。

四、在明确的场景下,为集合指定初始容量

集合类可以支持动态扩展长度,使用很方便,不过需要注意的是集合类实际存储使用的是数组,动态扩容也是通过Arrays.copyOf进行的。执行add操作时若默认长度不够时,会将集合的长度增加一定的倍数或长度(比如ArrayList会增加1.5倍),此时在数据量较大时进行拷贝会非常耗资源,效率也很低。
出于性能及资源使用考虑,在集合初始容量已知时,要直接初始为对应长度;在集合初始容量大概知道范围时,可以初始一个比最大值多一点的长度。

五、避开基本类型数组转换列表陷阱

书中举了一个非常好的例子,如下代码

int [] data = {1,2,3,3}; 
System.out.println(Arrays.asList(data).size()); 

 结果打印出的结果却只有一条,因为asList方法的参数为泛型参数,而基本类型不能作为泛型参数,而int数组是一个对象,同时也是可以泛型化的,所以asList(data)最后实际是把数组对象转换成列表了,结果打印就是1条。
所以一定要注意基本数据类型数组不能做为asList的输入参数,否则会引起程序逻辑混乱。

六、asList方法产生的List对象不可更改

Arrays.asList方法产生的List对象为Arrays类中的静态内部类ArrayList,其修改内容的方法(add、remove)是继承自父类的,而父类的方法仅仅是抛出异常,所以一定要注意asList产生的不可更改,不然会抛异常。

七、不同的列表选用不同的遍历算法

对于支持随机存储(实现RandomAccess接口)的列表,使用下标遍历要比使用foreach遍历快些,在数据量大时尤为明显。而对于顺序存储的列表(例如LinkedList)此时使用foreach又比使用下标遍历效率高些。
具体原因与遍历的实现代码有关,foreach是通过iterator进行遍历的,可以这么理解随机遍历的列表本来就希望下标访问,所以下标访问更快。而有序列表是有顺序的,所以通过foreach一个个迭代效率更高。当然实际原因是实现逻辑的问题。

八、频繁插入和删除时使用LinkedList

这个在数据结构里面肯定提到过,ArrayList这种随机列表随机存取效率高,而LinkedList这种有序列表插入和删除效率更高。即对于列表增、删操作多的应该选用LinkedList,而对于列表修改多的应该选用ArrayList。

九、判断列表相等时只用关心集合是否相等

集合元素进行相等判断(equals)时,可能是不同的集合类型,但是只要集合里面的元素相等则两个集合相等。

十、子列表只是原列表的一个视图

subList产生的列表只是一个视图,所有列表的改动都会直接作用于原列表上。

十一、生成子列表后不要再操作原列表

通过subList生成子列表后,如果再操作原列表会导致subList产生的列表调用size方法时抛出异常。对于子列表,因为视图是动态生成的,生成子列表后再操作原列表,必须导致视图的不稳定性,所以生成子列表后,最好是通过Collections.unmodifiableList方法设置列表为只读状态,这样就能避免错误的发生。

十二、不推荐使用binarySearch对列表进行检索

对一个列表进行检索时,使用的最多的是indexOf方法,该方法是通过遍历进行查找的;Collections类还提供一个binarySearch方法进行检索,该方法是通过二分查找法进行查找的,但是二分查找法有一个前提需要注意,就是列表必须是有序的,不然查找的结果不准确。 因此对于有序列表,binarySearch方法可以优先考虑,而对于无序列表不推荐使用。

十三、集合中的元素必须做到compareTo与equals同步

如果对象实现了compareTo方法就应该覆写equals方法,确保两者同步,不然可能产生逻辑混乱。

十四、集合运算时使用更优雅的方式

例如求并集可以直接使用list1.addAll(list2);求交集可以直接使用list1.retainAll(list2);求差集(属于A但不属于B的集合叫做A与B的差集);求不重复的集合(A与B不重复的集合为A与B的并集去掉一份重复的A与B的交集,如果交集有的话)可以先删后加:list2.remove(list1),list1.addAll(list2)

十五、多线程使用Vector或HashTable

 Vector是ArrayList的多线程版本,HashTable是HashMap的多线程版本。在多线程环境下更适用。

 

 

你可能感兴趣的:(高质量代码,集合,数组)