前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Java集合的subList方法分析

Java集合的subList方法分析

作者头像
IT云清
发布2019-01-22 11:21:29
1.9K0
发布2019-01-22 11:21:29
举报
文章被收录于专栏:IT云清
本文研究List集合的subList方法,测试方式为:新建一个集合,然后截取原集合的部分元素,然后去操作新集合和原集合来观察结果。

1.新集合中添加元素

代码语言:javascript
复制
public static void testSubList(){
        List list = new ArrayList<>();
        list.add(1);
        list.add(2);
        list.add(3);
        list.add(4);
        list.add(5);
        list.add(5);
        list.add(6);

        //截取集合
        List subList = list.subList(0, 3);
        System.out.println(subList);
        //截取的新集合添加元素
        subList.add(33);

        System.out.println(subList);
        System.out.println(list);
    }

结果为:

代码语言:javascript
复制
[1, 2, 3]
[1, 2, 3, 33]
[1, 2, 3, 33, 4, 5, 5, 6]

结论:我们会发现,虽然list 和 subList是两个不同的对象,但是我们在操作新集合时,发现原集合的数据也改变了,看一下源码:

代码语言:javascript
复制
        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;
        }

这里的parent是原集合,sunList返回的实际上是原集合的部分视图,所以你去修改新集合时,实际是把原集合改了。

2.原始集合添加元素

代码语言:javascript
复制
    public static void testSubList2(){
        List list = new ArrayList<>();
        list.add(1);
        list.add(2);
        list.add(3);
        list.add(4);
        list.add(5);
        list.add(5);
        list.add(6);

        //截取集合
        List subList = list.subList(0, 3);
        System.out.println(subList);
        System.out.println(list);

        //原始集合添加元素
        list.add(1000);

        System.out.println(list);
        //System.out.println(subList);
    }

结果:

代码语言:javascript
复制
[1, 2, 3]
[1, 2, 3, 4, 5, 5, 6]
[1, 2, 3, 4, 5, 5, 6, 1000]

如果再去打印subList,会报错:

代码语言:javascript
复制
java.util.ConcurrentModificationException

3.报错分析

这个打印输出为什么会报错呢?因为在打印时,其实是在迭代元素然后拼接后打印输出,在迭代的时候,出现了这个错误,我们按照调用链详细的追溯一下源码:

打印对象

代码语言:javascript
复制
    public void println(Object x) {
        String s = String.valueOf(x);
        synchronized (this) {
            print(s);
            newLine();
        }
    }

对象要toString()成字符串

代码语言:javascript
复制
    public static String valueOf(Object obj) {
        return (obj == null) ? "null" : obj.toString();
    }

打印的是集合,那转字符串时要迭代出元素才行,得有个迭代器

代码语言:javascript
复制
    public String toString() {
        Iterator<E> it = iterator();
        if (! it.hasNext())
            return "[]";

        StringBuilder sb = new StringBuilder();
        sb.append('[');
        for (;;) {
            E e = it.next();
            sb.append(e == this ? "(this Collection)" : e);
            if (! it.hasNext())
                return sb.append(']').toString();
            sb.append(',').append(' ');
        }
    }

获取迭代器

代码语言:javascript
复制
        public Iterator<E> iterator() {
            return listIterator();
        }

获取List集合的迭代器

代码语言:javascript
复制
    public ListIterator<E> listIterator() {
        return listIterator(0);
    }

迭代器内部

代码语言:javascript
复制
        public ListIterator<E> listIterator(final int index) {
            checkForComodification();
            rangeCheckForAdd(index);
            final int offset = this.offset;

            return new ListIterator<E>() {
                int cursor = index;
                int lastRet = -1;
                int expectedModCount = ArrayList.this.modCount;

                public boolean hasNext() {
                    return cursor != SubList.this.size;
                }

                @SuppressWarnings("unchecked")
                public E next() {
                    checkForComodification();
                    int i = cursor;
                    if (i >= SubList.this.size)
                        throw new NoSuchElementException();
                    Object[] elementData = ArrayList.this.elementData;
                    if (offset + i >= elementData.length)
                        throw new ConcurrentModificationException();
                    cursor = i + 1;
                    return (E) elementData[offset + (lastRet = i)];
                }
                。。。。。。
        }        

迭代前要做个检查,看看集合有没有被改变,这两个值如果不相等,就抛出错误,这就是我们之前打印抛出的那个错误。

代码语言:javascript
复制
        private void checkForComodification() {
            if (ArrayList.this.modCount != this.modCount)
                throw new ConcurrentModificationException();
        }

由于在截取新集合时,原来集合的的size是7,那么在你subList截取的新集合中,这个modCount也是7,现在,我们在原集合中添加了元素,那原来集合的modCount已经成8了,而这个截取获得的集合,modCount是7,比较后不相等,所以抛出了错误。

4.subList方法源码解释

看一下源码中对此方法的解释:

代码语言:javascript
复制
/**
     * Returns a view of the portion of this list between the specified
     * <tt>fromIndex</tt>, inclusive, and <tt>toIndex</tt>, exclusive.  (If
     * <tt>fromIndex</tt> and <tt>toIndex</tt> are equal, the returned list is
     * empty.)  The returned list is backed by this list, so non-structural
     * changes in the returned list are reflected in this list, and vice-versa.
     * The returned list supports all of the optional list operations supported
     * by this list.<p>
     *
     * This method eliminates the need for explicit range operations (of
     * the sort that commonly exist for arrays).  Any operation that expects
     * a list can be used as a range operation by passing a subList view
     * instead of a whole list.  For example, the following idiom
     * removes a range of elements from a list:
     * <pre>{@code
     *      list.subList(from, to).clear();
     * }</pre>
     * Similar idioms may be constructed for <tt>indexOf</tt> and
     * <tt>lastIndexOf</tt>, and all of the algorithms in the
     * <tt>Collections</tt> class can be applied to a subList.<p>
     *
     * The semantics of the list returned by this method become undefined if
     * the backing list (i.e., this list) is <i>structurally modified</i> in
     * any way other than via the returned list.  (Structural modifications are
     * those that change the size of this list, or otherwise perturb it in such
     * a fashion that iterations in progress may yield incorrect results.)
     *
     * @param fromIndex low endpoint (inclusive) of the subList
     * @param toIndex high endpoint (exclusive) of the subList
     * @return a view of the specified range within this list
     * @throws IndexOutOfBoundsException for an illegal endpoint index value
     *         (<tt>fromIndex &lt; 0 || toIndex &gt; size ||
     *         fromIndex &gt; toIndex</tt>)
     */
    List<E> subList(int fromIndex, int toIndex);

我们翻译一下这个方法:

返回这个列表在指定的[fromIndex,toIndex)之间的视图。

如果fromIndex=toIndex,那么返回的列表为空。

返回的列表由这个列表支持,所以在返回列表中非结构性的改变会反映在这个列表中,反之亦然。

返回的集合支持这个集合所支持的所有的可选的集合操作。(这个怎么翻译都感觉怪怪的,但是这样翻译应该是可以理解意思的)

这个方法不需要显示的范围操作(数组中常见的操作),任何希望可以用来范围操作的一个集合可以通过一个截取视图来代替完整的集合。

下面的例句,从一个集合中移除一个范围的元素。

代码语言:javascript
复制
  list.subList(from, to).clear();

相似的语句可以用于indexOf,lastIndexOf,以及Collections集合中的所有算法都可以应用于subList.(这个,还请高人指点)

如果这个集合以任何方式发生了结构性修改,这个方法返回的集合将会变成未定义的,除了通过这个返回的集合。

总结起来就是:这个方法返回的其实是原集合的一个视图,你在这个集合上的操作,会影响原来的集合;而如果原来的集合发生了改变,那么这个集合将会变得不可用。代码验证如上。
modCount是什么呢?下文:Java中modCount的用法,fail-fast机制
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2018年06月07日,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 本文研究List集合的subList方法,测试方式为:新建一个集合,然后截取原集合的部分元素,然后去操作新集合和原集合来观察结果。
  • 1.新集合中添加元素
  • 2.原始集合添加元素
  • 3.报错分析
  • 4.subList方法源码解释
    • 总结起来就是:这个方法返回的其实是原集合的一个视图,你在这个集合上的操作,会影响原来的集合;而如果原来的集合发生了改变,那么这个集合将会变得不可用。代码验证如上。
      • modCount是什么呢?下文:Java中modCount的用法,fail-fast机制
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档