赞
踩
经过了两个月的【深入学习Java编程方法】课程学习,对Java的一些新特性有了更多的了解。依旧,坑洼遍地。
Java中的Collection类是一个很好用、很常用的类,省去了当年C语言手动链表的麻烦之后,感觉整个人都变懒了。有些时候我们需要将某个或者某几个元素作为Collection类来进行传参之类的操作的时候,new一个新List或者Set再挨个add就感觉十分麻烦。从老师的PPT中学到了一招Arrays.asList(T... a)之后,感觉传参舒爽了很多。但是,在经历了一系列艰苦卓绝的Debug之后,终于找到了这个舒适的方法背后的阴险之处。
先说说从别人那里得到的一些结论:
- public static <T> List<T> asList(T... a) {
- return new ArrayList<>(a);
- }
看起来好像没什么问题,但是查看了import之后才发现,这个ArrayList不是java.util包下的,而是java.util.Arrays.ArrayList!意思就是,它返回的是Arrays自身的一个内部类,只是和util的那个重名了。这个类的成员变量E[] a是final类型,类本身的内部充满了各种各样的Observer,唯一的一个set(int index, E element)是需要一对一进行替换的方法。因此,这个类内部并没有实现Collection所拥有的add和remove方法。由于成员变量是一个final类型的数组,因此其长度也是不可变的。可以初步认为,asList生成的List给了用户一个【我真的创建了一个Collection】的错觉,实际上这个方法返回的List的本质还是一个数组,并且长度与调用时传入的元素个数相同,不可加长也不可缩短。这种欺骗消费者的行为着实不厚道。
这种结构导致某些情境下本来对List可行的操作出错。这里是我遇到的一种情况。
- public class test {
- Map<String, List<Integer>> map = new HashMap<>();
- String test = "test";
- int test1 = 1;
- int test2 = 2;
- map.put(test, Arrays.asList(test1, test2));
- List<Integer> takeout = map.get(test);
- int test3 = 3;
- takeout.add(test3); // 运行到这里报错
- }
- }
我使用asList向map中传入了一个List,然后我将其取出,进行List的add操作,运行时则会报错:
Exception in thread "main" java.lang.UnsupportedOperationException
at java.util.AbstractList.add(AbstractList.java:148)
at java.util.AbstractList.add(AbstractList.java:108)
at test.test.main(test.java:48) // 对应 takeout.add(test3) 这行
也就是说,即使我定义这个列表的类型是List,我把它放进Map中再拿出来,还是会导致无法及进行正常的add操作。remove操作同理。
知道了这一点,以后如果创建的List仅需要在其基础上进行遍历的话,asList是可以的。但是如果涉及到修改List的元素的话,请不要使用这个方法创建List,因为他tnn的就是一个数组!
常见异常:java.lang.UnsupportedOperationException。
接下来看看第三条。asList的参数可以是一个数组,传进去数组的话相当于进行了一个数组与Collection的连接。但是,这两者是“同生共死”的关系,修改了其中的哪一个,另一个都会发生相应的变化。看个例子:
- public class aslist {
- public static void main(String[] args) {
- Integer[] a_Integer = new Integer[] { 1, 2, 3, 4 };
- List a_Integer_List = Arrays.asList(a_Integer);
- foreach(a_Integer_List);
-
- a_Integer_List.set(0, 0);
- foreach(a_Integer_List);
- foreach(a_Integer);
-
- a_Integer[0] = 5;
- foreach(a_Integer_List);
- foreach(a_Integer);
- }
- /* 打印方法 */
- private static void foreach(List list) {
- for (Object object : list) {
- System.out.println(object + " ");
- }
- System.out.println();
-
- }
-
- private static void foreach(int[] a_int) {
- for (int i : a_int) {
- System.out.println(i + " ");
- }
- System.out.println();
- }
运行结果:
- 1
- 2
- 3
- 4
-
- 0
- 2
- 3
- 4
-
- 0
- 2
- 3
- 4
-
- 5
- 2
- 3
- 4
-
- 5
- 2
- 3
- 4
可以看到,当修改数组a_Integer的时候,a_Integer_List中下标相同的元素发生了相同的改变;反之用set方法修改a_Integer_List亦然。这是一个需要注意的地方,使用时要额外注意两者同步修改可能发生的异常。
最后一条,在介绍第三条的时候可能有人发现了,案例中创建的数组是Integer类型而非int类型的。如果我们这样调用asList方法:
- int[] a_int = { 1, 2, 3, 4 };
- List a_int_List = Arrays.asList(a_int);
- foreach(a_int_List);
输出结果为:[I@15db9742
(实机操作视控制台显示真实值为准)
这里的asList并没有将a_int视作一个int类型的数组,而是将这个数组本身当成了一个元素,创建成了一个以数组为元素的List。并且,在这种情况下,是无法调用set方法来同步修改数组的值的。因此,使用基本数据类型的时候,需要创建它的包装类的数组,再调用asList才能尽可能遇到少的错误。
常见异常:java.lang.ArrayStoreException。
到此,我在实验中以及网络中获取的关于asList的陷阱介绍就告一段落。
稍微总结一点的话,如果在实际使用过程中,需要创建或者传递一个只需要进行遍历操作的Collection的话,Arrays.asList()是一个十分方便的方法,可以节省很多对于列表的操作。但是如果需要在这个结构上进行添加、删除元素的操作的话,就不要使用这用方式创建List、Set等了。
所以说,不该偷懒的时候还是要逼一下自己的(¬_¬)
部分文字及代码来源:
https://blog.csdn.net/cntanghai/article/details/7188296
https://blog.csdn.net/keketrtr/article/details/47108435
http://www.cnblogs.com/Miracle-Maker/p/6360069.html
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。