一起来愉快的踩坑之旅——List
由于在平时编程中常用到List,但是List里面还有很多不为人知的坑,下面就来总结下常见的一些坑
Arrays$ArrayList到底你是谁
问题代码
public static void main(String[] args) {
String[] arrays ={"1","2","3"};
List<String> list = Arrays.asList(arrays);
list.add("4");
}
分析
看似很简单的代码,使用Arrays.asList将一个数组转化为List然后再添加一个元素,十分正常但是会抛出异常如下:
为啥我返回的ArrayList添加一个元素会报错?决定Debug进去看一下:
原来用Arrays.aslist得到的不是一个ArrayList而是一个Arrays的一个内部类
add和remove方法实际都来自AbstractList而java.utilArrays$ArrayList并没有重写父类的方法,而会抛出UnsupportedOperationException
。这就是为啥不支持增删的原因。
java.util.Arrays A r r a y L i s t ( ArrayList ( ArrayList(表示内部类的意思,下面的源码是Arrays内部类*ArrayList 的类型定义源码,在**java.util.Arrays.class文件中***)
list互相影响?
上面使用Arrays.asList(arrays)获取,除了不支持增删改查还会有一个大问题,就是原数组和新生成的内部数组会互相影响
public static void main(String[] args) {
String[] arrays ={"1","2","3"};
List<String> list = Arrays.asList(arrays);
list.set(0,"a");
arrays[2] = "c";
System.out.println("arrays:"+Arrays.toString(arrays));
System.out.println("list"+list);
}
结果:
可以看到,不管是修改原数组还是新的list集合两者都会互相影响。
是因为这个方法实现的时候使用了原始的数组
解决问题
-
套娃一层ArrayList
List<String> list = new ArrayList<>(Arrays.asList(arrays));
但是十分的麻烦
-
谷歌提供的Guava Lists 方法
List<String> list = Lists.newArrayList(arrays);
这两种方法都可以将新的List和原始数组解耦,不影响互相使用,同时由于生成的是正在的ArrayList,可以正常使用
与Arrays.asList一样的subList方法产生的新集合也会和原始的List集合互相影响。由于SubList内部有一个parent字段保存了最原始的List。
而且由于SubList还在引用原始的List所以容易造成OOM问题。由于每个SubList都会强引用一个10万个元素的原始List导致GC无法回收
foreach 增加/删除元素大坑
代码:
public static void main(String[] args) {
String[] arrays = {"1","2","3"};
List<String> list = new ArrayList<String>(Arrays.asList(arrays));
for(String str:list){
if (str.equals("1")){
list.remove(str);
}
}
}
看似很正常,然是却报错
可以清楚的看到程序的最终错误是由ArrayList$Itr.next处抛出,但是我们没有调用该方法,实际上是由于foreach这种方法是一种语法糖,其实际上就是Iterator迭代器实现方法。
解决方法
-
使用Iterator 的remove方法删除元素
public static void main(String[] args) { String[] arrays = {"1","2","3"}; List<String> list = new ArrayList<String>(Arrays.asList(arrays)); Iterator<String> iterator =list.iterator(); while (iterator.hasNext()){ String str = iterator.next(); if("1".equals(str)){ iterator.remove(); } } list.toString(); }
-
JDK1.8 List removeIf
public static void main(String[] args) { String[] arrays = {"1","2","3"}; List<String> list = new ArrayList<String>(Arrays.asList(arrays)); list.removeIf(str->str.equals("1")); System.out.println(list.toString()); }
t(arrays));
list.removeIf(str->str.equals(“1”));
System.out.println(list.toString());
}