博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
java ArrayList中的subList方法
阅读量:7126 次
发布时间:2019-06-28

本文共 6957 字,大约阅读时间需要 23 分钟。

hot3.png

  • 本文是本人的学习笔记,把自己的理解总结记录下来。因本人水平有限,如果您在阅读中发现错误,还望谅解,并且希望能够告知本人改正,不胜感激!

  • ArrayList中的subList()方法

    ArrayList中的subList(int fromIndex, int toIndex)方法,是返回当前ArrayList中从索引为fromIndex的位置索引位置为toIndex-1的位置的列表 视图。这里需要注意几点:

    1. subList方法传入的参数是左闭右开的,也就是[formIndex,toIndex)的形式。所以如果传来两个相同的index进去会返回一个空的列表。

      public class SubListTest {    public static void main(String[] args) {        List
      list = new ArrayList
      (); list.add("1"); list.add("2"); list.add("3"); list.add("4"); List
      subList = list.subList(1, 1); System.out.println("subList: " + subList); // subList: [] }}
    2. subList方法返回的只是一个视图,是一个对源列表的映射。这意味着如果对subList返回的列表进行编辑操作,源列表也会收到影响。

    3. 对subList方法返回的列表进行添加、删除元素会抛出异常。

    public class SubListTest { 		public static void main(String[] args) { 		List
    ; list = new ArrayList
    (); list.add("1"); list.add("2") list.add("3"); list.add("4"); List
    subList = list.subList(0, 2); System.out.println(subList); list.remove(1); System.out.println("subList:" + subList); System.out.println("list:" + list); } }

    抛出异常:

    Exception in thread "main"  java.util.ConcurrentModificationException	at java.util.ArrayList$SubList.checkForComodification(ArrayList.java:1239)	at java.util.ArrayList$SubList.listIterator(ArrayList.java:1099)	at java.util.AbstractList.listIterator(AbstractList.java:299)	at java.util.ArrayList$SubList.iterator(ArrayList.java:1095)	at java.util.AbstractCollection.toString(AbstractCollection.java:454)	at java.lang.String.valueOf(String.java:2994)	at java.lang.StringBuilder.append(StringBuilder.java:131)	at com.lijunpeng.List.SubListTest.main(SubListTest.java:23)

    这是因为触发fail-fast机制,导致抛出异常,关于fail-fase机制,可以参见这篇博客:

  • subList方法的源码

    public List
    subList(int fromIndex, int toIndex) { // 判断参数的合法性 subListRangeCheck(fromIndex, toIndex, size); // 返回一个SubList实例 return new SubList(this, 0, fromIndex, toIndex);}

    该方法会返回一个SubList的实例,SubList是一个ArrayList的内部类,其构造方法如下:

    // SubList中的属性    private final AbstractList
    parent; private final int parentOffset; private final int offset; int size; // 构造方法 SubList(AbstractList
    parent, int offset, int fromIndex, int toIndex) { this.parent = parent; this.parentOffset = fromIndex; this.offset = offset + fromIndex; this.size = toIndex - fromIndex; // 这个参数就是触发fail-fast机制的关键 this.modCount = ArrayList.this.modCount; }

    可以看到,SubList中并没有数组、列表之类的属性来存储数据,这进一步说明了,subList方法返回的只是一个视图。 之前说过,对subList方法返回的列表进行修改,源列表也会跟着发生变化,下面是SubList的set方法

    public E set(int index, E e) {        // 传入的参数索引合法性校验        rangeCheck(index);        // 这个方法就是用来检验modCount从而触发fail-fast机制,        // 但是set这里modCount不会发生变化,所以不会触发fial-fast机制        checkForComodification();        // 从外部类(源列表)获取值        E oldValue = ArrayList.this.elementData(offset + index);        // 讲外部类(源列表)的值替换成新的值,ArrayList的值是存在elementData的数组中的。        ArrayList.this.elementData[offset + index] = e;        return oldValue;    }

    从上面代码看出,修改subList的值其实是在修改源列表的值的,所以源列表值发生变化是必然的。 接下来再看一下为什么在源列表中删除或添加会抛出异常呢,首先我们先看一下之前报错的信息:

Exception in thread "main" java.util.ConcurrentModificationException            at java.util.ArrayList$SubList.checkForComodification(ArrayList.java:1239)            at java.util.ArrayList$SubList.listIterator(ArrayList.java:1099)            at java.util.AbstractList.listIterator(AbstractList.java:299)            at java.util.ArrayList$SubList.iterator(ArrayList.java:1095)            at java.util.AbstractCollection.toString(AbstractCollection.java:454)            at java.lang.String.valueOf(String.java:2994)            at java.lang.StringBuilder.append(StringBuilder.java:131)            at com.lijunpeng.List.SubListTest.main(SubListTest.java:23)

首先报错的是main函数的23也就是:System.out.println("subList:" + subList) 这一句。这一步并没有操作subList,是调用了其toString方法,进一步调用了SubList的迭代器方法。所以需要往下看错误信息,发现在checkForComodification这个方法中报错,上面在set方法中也有这个方法,并且这个方法就是触发java的fail-fast机制。所以看一下这个checkForComodification方法的代码:

private void checkForComodification() { 		if (ArrayList.this.modCount != this.modCount) 		throw new ConcurrentModificationException(); 	}

checkForComodificatio中比较了SubList类中的modCount和外部类ArrayList中的modCount是否相等,不相等就会抛出ConcurrentModificationException,触发fail-fast机制。那为什么这两个modeCount不相等呢?首先需要再回顾一下SubList类中的构造函数:

// SubList的构造函数 		SubList(AbstractList parent, int offset, int fromIndex, int toIndex) {		this.parent = parent; 		this.parentOffset = fromIndex; 		this.offset = offset + fromIndex; 		this.size = toIndex - fromIndex; 		// 在构造时,SubList中的modCount和ArrayList中的modCount是一致的 		this.modCount = ArrayList.this.modCount; 		}

接着,看一下ArrayList中的add方法,注意:代码中调用的是list.add而不是subList.add,所以需要看ArrayList的add源码:

public boolean add(E e) {		ensureCapacityInternal(size + 1);		elementData\[size++\] = e;		return true;	}    private void ensureCapacityInternal(int minCapacity) {		ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));	}    private void ensureExplicitCapacity(int minCapacity) { 		// 这里ArrayList的modCount发生了变化!		modCount++;		if (minCapacity - elementData.length > 0)		grow(minCapacity);	}

示例代码中:在list.add("5"),之后就直接打印了,也就是说我们没有再对SubList进行操作,SubList中的modCount也没有发生改变,所有 ArrayList.this.modCount != this.modCount 成立,导致抛出了ConcurrentModificationException异常。这里不仅打印会抛出异常,只要对subList进行操作都会抛出,因为SubList的方法都进行了checkForComodificatio,检查这两个modCount。

但是如果只对subList方法返回的列表:subList进行增删元素的操作,并不会抛出异常,但是同样会影响源列表,因为subList的所有操作都是在操作源列表:

public class SubListTest {        public static void main(String[] args) {            List
list = new ArrayList
(); list.add("1"); list.add("2"); list.add("3"); list.add("4"); List<String> subList = list.subList(0, 2); System.out.println(subList); subList.add("3"); System.out.println("subList:" + subList); // subList:[1, 2, 3] System.out.println("list:" + list); // list:[1, 2, 3, 3, 4] }}
  • 如果想要对subList的操作,可以新建一个ArrayList,将subList addAll进这个新的列表,从而操作这一个新的列表。
public class SubListTest {     public static void main(String[] args) {         List
list = new ArrayList
(); list.add("1"); list.add("2"); list.add("3"); list.add("4"); List
subList = list.subList(0, 2); List
newList = new ArrayList
(); newList.addAll(subList); newList.set(0, "new1"); System.out.println("list:" + list); // list:[1, 2, 3, 4] System.out.println("subList:" + subList); // subList:[1, 2] System.out.println("newList:" + newList); // newList:[new1, 2] } }
  • 小结

    • ArrayList.subList方法返回的只是视图
    • subList的所有操作都是在操作源列表ArrayList,所以增删改都会以影响源列表。
    • 对ArrayList进行添加、删除的操作之后,在操作subList会抛出异常
    • 仅仅subList操作并不会抛出异常

转载于:https://my.oschina.net/u/3428296/blog/1838962

你可能感兴趣的文章
tensorflow入门简单卷积神经网络
查看>>
我的友情链接
查看>>
the dude 使用教程和一点感觉
查看>>
Spring4+Hibernate4 注解整合配置
查看>>
螺旋数字程序
查看>>
Pyhton 第九章 正则表达式
查看>>
mysql主从配置
查看>>
Jconsole远程监控tomcat 的JVM内存(linux、windows)
查看>>
分布式项目(一)iot-pt
查看>>
JFreeChart开源图表组件在Java开发中的应用(一)
查看>>
使用ZooKeeper ACL特性进行znode控制
查看>>
struts2 跳转类型介绍 result type=chain、dispatcher、redirect(redirect-action)
查看>>
宜春之行
查看>>
我的友情链接
查看>>
Exchange2010 dag 的缷载
查看>>
2011/11/14 1:52 坚持就会胜利
查看>>
oracle概念和术语 建表时的一些参数pctfree initrans maxtrans sto
查看>>
我的友情链接
查看>>
转ApplicationContext的三种实现方式以及在web.xml配置的两种方式
查看>>
【我的软考之路】我的网工备考之路~
查看>>