1. 集合转换为类型一致的数组
使用集合转数组的方法,必须使用集合的 toArray(T[] array)方法,传入的是类型完全一致、长度为 0 的空数组。
反例:
直接使用 toArray 无参方法存在问题,此方法返回值只能是 Object[]类,若强转其它类型数组将出现ClassCastException 错误。
List<String> list=new ArrayList<>();
list.add("aa");
list.add("bb");
list.add("cc");
// 使用无参的toArray()方法,默认的返回类型是Object []
Object[] objects = list.toArray();
正例:
List<String> list = new ArrayList<>(2);
list.add("guan");
list.add("bao");
String[] array = list.toArray(new String[0]);
说明:使用 toArray 带参方法,数组空间大小的 length:
1) 等于 0,动态创建与 size 相同的数组,性能最好。
2) 大于 0 但小于 size,重新创建大小等于 size 的数组,增加 GC 负担。
3) 等于 size,在高并发情况下,数组创建完成之后,size 正在变大的情况下,负面影响与 2 相同。
4) 大于 size,空间浪费,且在 size 处插入 null 值,存在 NPE 隐患
2. 数组转换为类型一致的集合
场景:现在有一个int [] arr,希望转换为一个List。
// 数组转换为类型一致的集合
int [] arr1={1,2,3};
// 注意:这里直接传入int [] 的数组,返回的List的泛型参数为 int [],而不是Integer
List<int[]> list1 = Arrays.asList(arr1);
Integer [] arr2={1,2,3};
// 传入的参数类型为 Integer时,返回泛型参数就是Integer
List<Integer> list2 = Arrays.asList(arr2);
注意:
很容易犯的一个错误就是,使用Arrays.asList()传入int [] 数组,然后用List < Integer>去接受,报错为类型不一致。
Arrays.asList的返回值:
正如上面所看到的,Arrays.asList()会返回一个List。现在我们尝试使用这个list来add(),或者remove()来操作,发现报错了。
看一下Arrays.asList()的源码:
返回了个ArrayList,但是,该ArrayList并不是java.util.ArrayList,而是Arrays的一个内部类。
内部ArrayList的结构:
可以发现它并没有重写add(),remove()两个会改变数据结构的方法,所以当我们直接使用返回的List去add或者remove时,实际上调用的是他的父类AbstractList的add/remove方法:
可以看出这里的实现就是直接抛出了一个异常!
总结:
Arrays.asList()返回的实际上是一个
不允许修改其结构的List
(注:set(index,val)不属于改变结构的方法)。当前希望对返回的List进行修改结构操作时,按照下面的方式即可:
Integer [] arr2={1,2,3};
// 传入的参数类型为 Integer时,返回泛型参数就是Integer
List<Integer> list2 = Arrays.asList(arr2);
// 新建一个java.util包下的List的实现类即可
List<Integer> list=new ArrayList<>(list2);
list.add(4);
System.out.println(list);
注意:
看看下面的代码执行结果:
Integer [] arr2={1,2,3};
// 传入的参数类型为 Integer时,返回泛型参数就是Integer
List<Integer> list2 = Arrays.asList(arr2);
list2.set(0,111);
System.out.println(arr2[0]);
arr2[1]=222;
System.out.println(list2);
OK,也就是说,返回的List与原数组Integer [] arr2底层使用的是同一份数据。其实前的源码已经看到了这一点:
可以看出,调用Arrays.asList(arr)后,new 内部的ArrayList时直接把数组arr赋值给内部变量E [] a,后面调用get()时,也是直接操作的该内部变量a。
3. 另一个“假List的情形”:
public static void main(String[] args) {
List<Integer> list=new ArrayList<>();
list.add(0);
list.add(1);
list.add(2);
list.add(3);
list.add(4);
// 取左闭右开的子List
List<Integer> sub = list.subList(1, 3);
System.out.println(sub);// out:[1,2]
sub.add(555);
System.out.println(sub);// out:[1,2,555]
System.out.println(list);// out:[0,1,2,555,3,4]
list.add(666);
System.out.println(list);// out:[0,1,2,555,3,4,666]
// 发生fast-fail异常
System.out.println(sub);
}
List::subList()会返回一个List:
这个List的实际类型是ArrayList中的一个内部类:
该类是AbstractList的子类,即也是List的实现类,它实现了add/remove方法,故我们可以调用它的add/remove方法。**但是:**它的add与remove的实现逻辑与ArrayList并不相同,从上面的测试例子以及源码描述可以可看出返回的SubList与父List共用底层的同一个存储数组,所以修改SubList的结构,父List的结构也会发生改变。
另外:
list.add(666);
System.out.println(list);// out:[0,1,2,555,3,4,666]
// 发生fast-fail异常
System.out.println(sub);
对父List的
结构
修改后,当再次访问SubList时,会发生fast-fail异常。