sublist方法_SubList到底怎么转化为ArrayList?

06932647b3a763d3579e1a531596a16f.png

SubList

大家好,今天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异常。

ModificationException

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已经报异常了。

通俗一点,可以修改儿子,不能修改父亲。

SubList转化为ArrayList可以用Guava中的封装方法

Lists.newArrayList(subList)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值