前言:由于上一个星期工作繁忙,利用上下班和晚上睡前空余的时间拜读了秦小波老师的《改善Java程序的151建议》,感觉廓然开朗,注意到了很多平时在编写代码中并不会注意的问题,甚至感觉自己对Java只是略懂皮毛,不足以登大雅之堂,特此与读者分享读书笔记,以下内容摘自《改善Java程序的151建议》一书和笔者的理解 Java高质量代码系列文章 面向对象篇:http://ray-yui./blog/1926984 数据类型篇:http://ray-yui./blog/1927251 字符串篇:http://ray-yui./blog/1927647 数组与集合(1):http://ray-yui./blog/1928170 数组与集合(2):http://ray-yui./blog/1930155 还记得当初学习编程的时候,老师曾经说过,什么是程序?就是数据结构加算法,这句话当时并没有多大的体会,对编程逐渐熟悉后,回想起来,确实如此,而在Java中的数据结构,就体现在集合框架当中,而使用集合有哪些地方需要注意呢? 1.性能考虑,优先选择数组 数组在项目开发当中使用的频率是越来越少,特别是在业务为主的开发当中,首先数组没有List,Set等集合提供的诸多方法,查找增加算法都要自己编写,极其繁琐麻烦,但由于List,Set等集合使用泛型支持后,存放的都为包装类,而数组是可以使用基本数据类型,而使用基本数据类型的执行运算速度要比包装类型快得多,而且集合类的底层也是通过数组进行实现. 2.若有必要,使用变长数组 在学习集合类当中,很多人喜欢将数组的定长拿来和集合类型的自变长来做比较,但其实这种比较并不合适,通过观察集合类例如ArrayList的实现其实可以看出,所谓的集合变长,其实只是用婉转的方式对原数组进行了扩容 Java代码
当性能要求高的时候,可以考虑使用对数组进行封装使用,数组长度不变不是我们不使用它们的借口 3.警惕数组的浅拷贝 数组的浅拷贝在Java编程中亦是基础中的基础,浅拷贝是在为数组拷贝时,基本类型拷贝的是值,而引用类型拷贝的是引用地址,在上面的例子当中,拷贝数组使用的Arrays.copyOf为浅拷贝,在使用时需要注意 4.在明确的场景下,为集合指定初始容量 在我们平常的使用当中,因为集合类型是自动变长的,所以基本创建对象时不会为集合类附上初始值,就拿我们最常用的ArrayList来说明,我们首先要知道,当集合容量到达临界点时,会将底层的数组进行copyOf的操作,生成新的数组,而新的数组容量为旧数组的1.5倍,而默认数组长度为10,当我们明确知道要放置入容器中的数据数量较多时,应该指明初始值,避免多次使用copyOf造成的性能开销 5.选择合适的最值算法 对数据进行最大值或最小值的查找,这是数据结构最基本的知识,在Java当中我们亦有很多种的方式进行实现,以下列举2种算法 Java代码
Java代码
6.基本类型数组转换陷阱! 请观察以下代码 Java代码
我们期望的结果是将数组中的元素通过Arrays.asList转换到集合类当中,但事与愿违,我们只将数组本身增加了进入,并没有将数组内的值分拆分开来,此时若然对集合List增加了泛型就会在编译期间给出错误的提示,或将数组本身改变成Integer就可以解决问题 7.asList方法产生的List对象不可更改 通过上面的例子,我们可以看到使用Arrays.asList方法可以将一个数组转换成一个List,那通过asList方法返回的List有什么特别呢?注意,这个返回的List是不支持更改的,原因是因为asList方法返回的,并不是java.util.ArrayList,而是Arrays工具类中的一个静态私有内部类,虽然都有实现和ArrayList一样的父类AbstractList,但在复写add等方法时,却是抛出了UnsupportedOperationException, 这个静态私有内部类只实现了size,toArray,get,contains这几个方法 8.对不同的数据结构使用不同的遍历方式 请观看以下代码 Java代码
为什么对LinkedList和ArrayList要选择不同的遍历方式? 1.因为ArrayList实现了RamdomAccess接口(随机存取接口),RamdomAccess 接口和Serializable,Cloneable接口一样是Java中的标示接口,代表这个 这个类可以随机存取,对ArrayList来说就标志着,数据之间没有关联, 即相邻的两个位置没有互相依赖的关系,可以随机访问, 2.Java中的foreach语法是iterator(迭代器)的变形用法,我们知道迭代器 是23种设计模式的一种,但迭代器是需要知道两个元素时间的关系的,不然 怎么提供hasNext的支持呢?就是因为上一个元素要判断下一个元素是否 存在,强行建立了这种关系,违背了ArrayList随机存取的特别 3.在LinkedList中,因为是通过双向链表的形式来存储,所以对迭代器的 支持非常好,因为LinkedList相邻的两个元素本来就存在关系 所以在对LinkedList和ArrayList要采取不同的遍历方式,读者若然有兴趣 可以尝试一下对LinkedList采用下标的形式访问,会发现两者的效率有较大 的差距 8.适时选择ArrayList或LinkedList ArrayList和LinkedList的主要区别: 1.ArrayList底层的数据结构为数组,而LinkedList底层结构为双向链表 2.在插入数据时,由于ArrayList每次插入后都需要将数组元素向后顺延 位置,而LinkedList只需要更改头节点和尾节点即可完成插入操作,所以 在插入操作较为频繁时,优先使用LinkedList 3.在删除数据时,由于ArrayList要保持数组的有序性,当删除后元素要亦 需要向后或向前移位,而LinkedList照旧还是更改头尾节点. 4.在更新时,由于LinkedList会使用折半遍历的方式进行查找定位元素再 进行更新,对比起ArrayList的直接定位下标元素替换,ArrayList对更新 的效率更佳 5.LinkedList可以模拟队列,通过LinkedList的addFirst,addLast等操作 9.列表相等只需关心元素数据 Java为了我们可以安心的面向List,Set,Map等接口进行编程,因此对集合类中的equlas进行了复写,让我们在比较两个集合是否相等时,只需要比较元素数据是否相等即可,避免了因为替换集合实现类造成的错误 Java代码
总结: 笔者在本文章中只从《改善Java程序的151建议》中提取部分进行归纳性叙述,推荐各位读者购买这本书,该书不仅从事例中学习,而且涉及到原理,底层的实现,不仅告诉你应该怎么做,还告诉你为什么要这样做. |
|