赞
踩
大家好,今天Tony给大家讲个SubList
转化的坑。
这个错误真的会被忽略,大家好好的看看,这个错误我们生产环境还真的遇到过。
集合类型相信大家都很熟悉,在Java中ArrayList使用的场景非常普遍。我们今天主要看的是ArrayList中的subList 方法。
首先我们来看看源码
Returns a view of the portion of this list between the specified {@code fromIndex}, inclusive, and {@code toIndex}, exclusive.
在jdk的源码中清楚的写明了返回的是一个new SubList
,方法的注释上面写的是返回一个View,可以理解为视图吗?
- public List<E> subList(int fromIndex, int toIndex) {
- subListRangeCheck(fromIndex, toIndex, size);
- return new SubList(this, 0, fromIndex, toIndex);
- }
接下里我们再细品SubList
,源码
- private class SubList extends AbstractList<E> implements RandomAccess {
- private final AbstractList<E> parent;
- private final int parentOffset;
- private final int offset;
- int size;
-
- SubList(AbstractList<E> parent,
- int offset, int fromIndex, int toIndex) {
- this.parent = parent;
- this.parentOffset = fromIndex;
- this.offset = offset + fromIndex;
- this.size = toIndex - fromIndex;
- this.modCount = ArrayList.this.modCount;
- }
- }
SubList 是ArrayList中的一个内部类,继承了AbstractList,实现了RandomAccess,从上面的代码中可以看到,在SubList这个构造方法中还是直接引用的父类中的元素,只是单纯的将截取的索引重新赋值了一下。
使用场景
- public static void main(String[] args) {
- List<String> names = new ArrayList<String>() {{
- add("兔子");
- add("托尼");
- add("啊");
- }};
- List<String> subList = names.subList(0, 3);
- System.out.println(subList);
-
- }
上面的代码输出结果
[兔子, 托尼, 啊]
在什么情况下会报错呢?接下来再看个例子,把上面的代码简单修改下,让数据返回ArrayList
- public static void main(String[] args) {
- List<String> names = new ArrayList<String>() {{
- add("兔子");
- add("托尼");
- add("啊");
- }};
- ArrayList<String> subList = (ArrayList)names.subList(0, 3);
- System.out.println(subList);
- }
上面的代码直接抛出异常了
Exception in thread "main" java.lang.ClassCastException: java.util.ArrayList$SubList cannot be cast to java.util.ArrayList
为什么不能直接转换为ArrayList呢?上面的源码已经显示了,SubList只是一个内部类,它继承AbstractList 和ArrayList 根本都没有关系,所以直接转化会报Cast异常。
SubList同样具有集合原属都方法比如添加、删除等。我截取部分源码。
- public E set(int index, E e) {
- rangeCheck(index);
- checkForComodification();
- E oldValue = ArrayList.this.elementData(offset + index);
- ArrayList.this.elementData[offset + index] = e;
- return oldValue;
- }
-
- public E get(int index) {
- rangeCheck(index);
- checkForComodification();
- return ArrayList.this.elementData(offset + index);
- }
-
- public int size() {
- checkForComodification();
- return this.size;
- }
-
- public void add(int index, E e) {
- rangeCheckForAdd(index);
- checkForComodification();
- parent.add(parentOffset + index, e);
- this.modCount = parent.modCount;
- this.size++;
- }
-
- public E remove(int index) {
- rangeCheck(index);
- checkForComodification();
- E result = parent.remove(parentOffset + index);
- this.modCount = parent.modCount;
- this.size--;
- return result;
- }
-
- protected void removeRange(int fromIndex, int toIndex) {
- checkForComodification();
- parent.removeRange(parentOffset + fromIndex,
- parentOffset + toIndex);
- this.modCount = parent.modCount;
- this.size -= toIndex - fromIndex;
- }
-
- ```
-
- 上面等源码中每一个方法都包含有一个 checkForComodification 方法。
- 这个方法是有什么作用呢?
- ```java
- private void checkForComodification() {
- if (ArrayList.this.modCount != this.modCount)
- throw new ConcurrentModificationException();
- }
源码中写的很清楚,判断原始类型,可以理解为父类型原始的ArrayList和当前的SubList方法中的元素个数做比较,如果不一样就报异常。 1、 对subList视图做数据的删除
- public static void main(String[] args) {
- List<String> namesList = new ArrayList<String>() {{
- add("兔子");
- add("托尼");
- add("啊");
- }};
- System.out.println("namesList原始的:== ==>" + namesList);
- List<String> subList = namesList.subList(0, 2);
- System.out.println("subList截取的:== ==>" + subList);
- //删除SubList第2个元素
- subList.remove(1);
- System.out.println("subList删除的:== ==>" + subList);
- System.out.println("namesList删除的:== ==>" + namesList);
- }
上面的代码运行正常输出结果
- namesList原始的:== ==>[兔子, 托尼, 啊]
- subList截取的:== ==>[兔子, 托尼]
- subList删除的:== ==>[兔子]
- namesList删除的:== ==>[兔子, 啊]
2、 对ArrayList做数据的删除
- public static void main(String[] args) {
- List<String> namesList = new ArrayList<String>() {{
- add("兔子");
- add("托尼");
- add("啊");
- }};
- System.out.println("namesList原始的:== ==>" + namesList);
- List<String> subList = namesList.subList(0, 2);
- System.out.println("subList截取的:== ==>" + subList);
- //删除ArraList第2个元素
- namesList.remove(1);
- System.out.println("subList删除的:== ==>" + subList);
- System.out.println("namesList删除的:== ==>" + namesList);
- }
输出结果报异常了
- namesList原始的:== ==>[兔子, 托尼, 啊]
- subList截取的:== ==>[兔子, 托尼]
- Exception in thread "main" java.util.ConcurrentModificationException
- at java.util.ArrayList$SubList.checkForComodification(ArrayList.java:1231)
- at java.util.ArrayList$SubList.listIterator(ArrayList.java:1091)
- at java.util.AbstractList.listIterator(AbstractList.java:299)
- at java.util.ArrayList$SubList.iterator(ArrayList.java:1087)
- at java.util.AbstractCollection.toString(AbstractCollection.java:454)
- at java.lang.String.valueOf(String.java:2994)
- at java.lang.StringBuilder.append(StringBuilder.java:131)
当我们对父元素ArrayList中对数据进行删除操作的时候,我们会发现subList会报一个 ConcurrentModificationException异常,这个异常是对数据比较发现元素被更改过,可以理解为脏数据吗?
1、 SubList 和 ArrayList之间没有任何关系
2、千万不要将SubList转化为ArrayList会报转换异常
3、对SubList视图元素对修改会影响原始对父ArrayList中对数据。
4、对ArrayList数据对删除添加等修改,SubList会报Modification异常
其实我们可以理解下,SubList理解为一个视图,其实就是一个内部类,它的实现就是在原始的ArrayList中改变了截取的索引位置。
对视图的操作结果会反映到原始的ArrayList中,如果对原始的ArrayList做数据的添加删除操作,不好意思此刻的SubList已经报异常了。
通俗一点,可以修改儿子,不能修改父亲。
Lists.newArrayList(subList)
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。