当初的一些高级编程语言都会提供各种开箱即用的数据结构的实现,像 Java 编程语言的汇合框架中就提供了各种实现,汇合类蕴含 Map 和 Collection 两个大类,其中 Collection 上面的 List 列表是咱们常常应用的汇合类之一,很多的业务代码都离不开它,明天就来看看 List 列表的一些坑。
第一个坑:Arrays.asList 办法返回的 List 不反对减少、删除操作
例如咱们执行以下代码:
List<String> strings = Arrays.asList("m", "g");
strings.add("h");
会抛出 java.lang.UnsupportedOperationException
异样,此时你心田 OS what?明明返回的 ArrayList 为啥不能往里面减少元素,这当前还能好好的减少元素吗?
,而后果决开启 Debug
大法:
发现返回的 ArrayList
并不是咱们罕用的 java.util.ArrayList
,而是 Arrays
的外部类 java.util.Arrays.ArrayList
。进入办法 Arrays.asList
源码如下:
public static <T> List<T> asList(T... a) {return new ArrayList<>(a);
}
办法返回的是 Arrays
的动态外部类 java.util.Arrays.ArrayList
,该类尽管和 java.util.ArrayList
也继承自抽象类 java.util.AbstractList
,然而通过该类的源码发现它并没有对形象父类 AbstractList
的 add
办法默认就是抛出 java.lang.UnsupportedOperationException
异样。
这个坑的根本原因是咱们调用返回的 strings
的 add
办法是继承自形象父类的 add
办法,而形象父类的办法默认就是抛出 java.lang.UnsupportedOperationException
这个异样。
第二个坑,Arrays.asList 办法返回的新 List 和该办法原始入参数组批改会相互影响
Arrays.asList
办法除了下面这个 不反对减少、删除元素 这个坑之外,还有另外一个坑:
从以上代码能够发现,对原始数组的批改会影响咱们通过 Arrays.asList
办法取得的新 List
,深刻 java.util.Arrays.ArrayList
的源码:
private static class ArrayList<E> extends AbstractList<E>
implements RandomAccess, java.io.Serializable
{
private static final long serialVersionUID = -2764017481108945198L;
private final E[] a;
ArrayList(E[] array) {a = Objects.requireNonNull(array);
}
...
}
能够发现是间接应用了原始的数组,所有当咱们应用 Arrays.asList
形式取得的 List
时要特地留神,因为共享了数组,互相批改时可能产生一些意想不到的 Bug。规范的姿态之一是将其作为 ArrayList
构造方法的参数从新 new
一个 List
进去即可(e.g. List<String> stringList = new ArrayList<>(Arrays.asList(arrays))
)或者通过 Guava
库中的 Lists.newArrayList
,将返回的新 List
和原始的数组解耦,就不会再相互影响了。
第三个坑,间接遍历 List 汇合删除元素会报错
在间接遍历汇合元素时减少、删除元素会报错,比方执行如下代码:
List<String> stringList = Lists.newArrayList("m", "g", "h");
for (String s : stringList) {if (Arrays.asList("m", "h").contains(s)) {stringList.remove(s);
}
}
以上代码能够失常编译通过,然而执行时会抛出 java.util.ConcurrentModificationException
异样,查看其源码能够发现,删除元素办法 remove
会使汇合构造产生批改,也就是 modCount(
汇合理论批改的次数)会批改,在循环过程中,会比拟以后 List
的汇合理论批改的次数 modCount
与迭代器批改的次数 expectedModCount
,而 expectedModCount
是初始化时的 modCount
,二者不相等,就会报 ConcurrentModificationException
异样。解决办法次要有两种形式,1. 应用 ArrayList
的迭代器形式遍历,而后调用其中的办法。2. 在 JDK 1.8+ 能够应用 removeIf
办法进行删除操作。
最初扎心一问:调用 ArrayList
的 remove
办法传入 int
根本类型的数字和 Integer
包装类型的数字,执行后果是不是一样的?