数组8:解析《阿里巴巴开发手册》中ArrayList的subList的

Posted 纵横千里,捭阖四方

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了数组8:解析《阿里巴巴开发手册》中ArrayList的subList的相关的知识,希望对你有一定的参考价值。

​在《阿里巴巴Java开发手册》泰山版中关于subList有这样的两个描述

这里是啥意思呢?我们来分析一下:

先来看下subList的简单使用:

List<String> bookList = new ArrayList<>();bookList.add("面上头条快手阿里");bookList.add("升职加薪");bookList.add("当上CTO");bookList.add("迎娶白富美");bookList.add("走向人生巅峰");List<String> lqcBookList = bookList.subList(3, 5);System.out.println(bookList);System.out.println(lqcBookList);

运行结果如下图所示:

[面上头条快手阿里, 升职加薪, 当上CTO, 迎娶白富美, 走向人生巅峰][迎娶白富美, 走向人生巅峰]

从运行结果可以看出,subList返回的是bookList中索引从fromIndex(包含)到toIndex(不包含)的元素集合。

使用起来很简单,也很好理解,不过呢,要注意,修改原始集合会影响到子集合,如果修改子集合,会影响到原始集合。

比如,我们修改下原集合bookList中某一元素的值(非结构性修改):

     public static void testSubList() {        List<String> bookList = new ArrayList();        bookList.add("面上头条快手阿里");        bookList.add("升职加薪");        bookList.add("当上CTO");        bookList.add("迎娶白富美");        bookList.add("走向人生巅峰");        List<String> lBookList = bookList.subList(2, 5);        System.out.println("修改前:");        System.out.println("bookList:" + bookList);        System.out.println("lBookList:" + lBookList);        // 修改原集合的值        bookList.set(2, "CEO也行");        System.out.println("修改后:");        System.out.println("bookList:" + bookList);        System.out.println("lBookList:" + lBookList);    }

打印结果:

修改前:bookList:[面上头条快手阿里, 升职加薪, 当上CTO, 迎娶白富美, 走向人生巅峰]lBookList:[当上CTO, 迎娶白富美, 走向人生巅峰]修改后:bookList:[面上头条快手阿里, 升职加薪, CEO也行, 迎娶白富美, 走向人生巅峰]lBookList:[CEO也行, 迎娶白富美, 走向人生巅峰]

可以看到,我们修改原始的bookList,结果子集合lBookList也被修改了,“当上CTO”被换成了“CEO也行”。

另外一条:“修改原集合的结构,会引起ConcurrentModificationException异常”又是怎么回事呢?

比如,我们往原集合bookList中添加一个元素(结构性修改):

   public static void testSubList() {        List<String> bookList = new ArrayList();        bookList.add("面上头条快手阿里");        bookList.add("升职加薪");        bookList.add("当上CTO");        bookList.add("迎娶白富美");        bookList.add("走向人生巅峰");        List<String> lBookList = bookList.subList(2, 5);        System.out.println("修改前:");        System.out.println("bookList:" + bookList);        System.out.println("lBookList:" + lBookList);        //原始集合添加一项        bookList.add( "想想有点小激动");        System.out.println("修改后:");        System.out.println("bookList:" + bookList);        System.out.println("lBookList:" + lBookList);    }

我们可以看到在打印子list的时候抛异常了:

这里虽然原始list添加成功了,但是在执行打印子list的时候抛出了异常。

需要注意的是,以上异常并不是在添加元素时发生的,而是在添加元素后,遍历子集合时发生的。

那这是什么道理呢?还是看看源代码吧

首先,我们看下subList方法的注释,了解下它的用途:

Returns a view of the portion of this list between the specified {@code fromIndex}, inclusive, and {@code toIndex}, exclusive.

说人话就是:返回指定的fromIndex和toIndex之间的列表部分的视图。

然后,我们看下它的源码:

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的内部类,该构造函数中也并没有重新创建一个新的ArrayList,只是直接使用了原始List的游标。也就是说两者其实指向了同一个堆内存空间,SubList只是为了方便操作增加了新的指示游标而已。因此修改其中一个就会影响到另一个。在业务中这是很危险的,可能导致未知的错误,所以不能这么做。

总结

ArrayList的subList方法,返回的是原集合的一个子集合(视图),非结构性修改任意一个集合的元素的值,都会彼此影响,结构性修改原集合时,会报ConcurrentModificationException异常,结构性修改子集合时,会影响原集合,所以使用时要注意,避免程序错误或者异常。


 


 

以上是关于数组8:解析《阿里巴巴开发手册》中ArrayList的subList的的主要内容,如果未能解决你的问题,请参考以下文章

《阿里巴巴Java开发手册(正式版》读记

阿里巴巴开发手册(list集合操作)

阿里巴巴Java 开发手册编程规约之异常日志

GCC手册解析——变长数组

GCC手册解析——变长数组

foreach循环中不能使用remove删除元素的原理解析