编写高质量代码:改善Java程序的151个建议(第5章:数组和集合___建议65~69) 原
欢迎来到阿八个人博客网站。本 阿八个人博客 网站提供最新的站长新闻,各种互联网资讯。 喜欢本站的朋友可以收藏本站,或者加QQ:我们大家一起来交流技术! URL链接:https://www.abboke.com/jsh/2019/0718/8930.html
撸了今年阿里、网易和美团的面试,我有一个重要发现.......>>>
如果你浪费了自己的年龄,那是挺可悲的。因为你的青春只能持续一点儿时间——很短的一点儿时间。 —— 王尔德
建议65:避开基本类型数组转换列表陷阱
我们在开发中经常会使用Arrays和Collections这两个工具类和列表之间转换,非常方便,但也有时候会出现一些奇怪的问题,来看如下代码:
public class Client65 {
public static void main(String[] args) {
int data [] = {1,2,3,4,5};
List list= Arrays.asList(data);
System.out.println("列表中的元素数量是:"+list.size());
}
}
也许你会说,这很简单,list变量的元素数量当然是5了。但是运行后打印出来的列表数量为1。
事实上data确实是一个有5个元素的int类型数组,只是通过asList转换成列表后就只有一个元素了,这是为什么呢?其他4个元素到什么地方去了呢?
我们仔细看一下Arrays.asList的方法说明:输入一个变长参数,返回一个固定长度的列表。注意这里是一个变长参数,看源码:
public static <T> List<T> asList(T... a) {
return new ArrayList<>(a);
}
asList方法输入的是一个泛型变长参数,基本类型是不能泛型化的,也就是说8个基本类型不能作为泛型参数,要想作为泛型参数就必须使用其所对应的包装类型。
解决方法:
Integer data [] = {1,2,3,4,5};
把int替换为Integer即可让输出元素数量为5.需要说明的是,不仅仅是int类型的数组有这个问题,其它7个基本类型的数组也存在相似的问题,这就需要大家注意了,在把基本类型数组转换为列表时,要特别小心asList方法的陷阱,避免出现程序逻辑混乱的情况。
建议66:asList方法产生的List对象不可修改
上一个建议指出了asList方法在转换基本类型数组时存在的问题,接着我们看一下asList方法返回的列表有何特殊的地方,代码如下:
package OSChina.Client;
import java.util.Arrays;
import java.util.List;
public class Client5 {
public static void main(String[] args) {
// 五天工作制
Week days[] = { Week.Mon, Week.Tue, Week.Wed, Week.Thu, Week.Fri };
// 转换为列表
List<Week> list = Arrays.asList(days);
// 增加周六为工作日
list.add(Week.Sat);
/* do something */
}
}
enum Week {
Sun, Mon, Tue, Wed, Thu, Fri, Sat
}
UnsupportedOperationException,不支持的操作,居然不支持list的add方法,这是什么原因呢?
此ArrayList非java.util.ArrayList,而是Arrays工具类的一个内部类
我们深入地看看这个ArrayList静态内部类,它仅仅实现了5个方法:
① size:元素数量
② get:获得制定元素
③ set:重置某一元素值
④ contains:是否包含某元素
⑤ toArray:转化为数组,实现了数组的浅拷贝
对于我们经常使用list.add和list.remove方法它都没有实现,也就是说asList返回的是一个长度不可变的列表,数组是多长,转换成的列表也就是多长,换句话说此处的列表只是数组的一个外壳,不再保持列表的动态变长的特性,这才是我们关注的重点。有些开发人员喜欢这样定义个初始化列表:
List<String> names= Arrays.asList("张三","李四","王五");
一句话完成了列表的定义和初始化,看似很便捷,却隐藏着重大隐患---列表长度无法修改。想想看,如果这样一个List传递到一个允许添加的add操作的方法中,那将会产生何种结果,如果有这种习惯的javaer,请慎之戒之,除非非常自信该List只用于只读操作。
建议67:不同的列表选择不同的遍历算法
测试来看简单for循环比foreach能快那么一丢丢。
建议68:频繁插入和删除时使用LinkedList
ArrayList在进行插入元素时:
public void add(int index, E element) {
//检查下标是否越界
rangeCheckForAdd(index);
//若需要扩容,则增大底层数组的长度
ensureCapacityInternal(size + 1); // Increments modCount!!
//给index下标之后的元素(包括当前元素)的下标加1,空出index位置
System.arraycopy(elementData, index, elementData, index + 1,
size - index);
//赋值index位置元素
elementData[index] = element;
//列表长度加1
size++;
}
注意看arrayCopy方法,只要插入一个元素,其后的元素就会向后移动一位,虽然arrayCopy是一个本地方法,效率非常高,但频繁的插入,每次后面的元素都要拷贝一遍,效率变的就低了。而使用LinkedList就显得更好了,LinkedList是一个双向列表,它的插入只是修改了相邻元素的next和previous引用。
原理不说了。
修改元素:LinkedList不如ArrayList,因为LinkedList是按顺序存储的,因此定位元素必然是一个遍历过程,效率大打折扣。而ArrayList的修改动作是数组元素的直接替换,简单高效。
LinkedList:删除和插入效率高;ArrayList:修改元素效率高。
建议69:列表相等只关心元素数据
判断集合是否相等只须关注元素是否相等即可,不用看容器