javaAPI深入理解(1)如何截断一个List以及List.subList()方法的坑

来源:互联网 发布:标准c语言void main 编辑:程序博客网 时间:2024/05/21 17:14

removeRange(int, int);这个方法AbstractList并没有暴露出来,我们应该如何得到一个截短的list?

1 如何得到一个list某个范围的子集sublist

首先想到sublist(int, int)方法 
注意此方法参数左闭右开。 
测试如下

1.1 修改sublist会影响原来的list

    LinkedList<String> ll = new LinkedList<>();    ll.add("a");    ll.add("b");    ll.add("c");    List<String> l2 =  ll.subList(1, 2);//[)    l2.add("new");    System.out.println(ll);    System.out.println(l2);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

[a, b, new, c] 
[b, new] 
可见sublist是快照,sulist插入会影响原list

1.2 修改原list,则sublist的所有操作会报错

    LinkedList<String> ll = new LinkedList<>();    ll.add("a");    ll.add("b");    ll.add("c");    List<String> l2 =  ll.subList(1, 2);//[)    ll.add("d");    System.out.println(ll);    System.out.println(l2);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

Exception in thread “main” java.util.ConcurrentModificationException 
at java.util.SubList.checkForComodification(AbstractList.java:769) 
可见如果更改了原来的list,sublist的任何操作都会报错,包括get() size(),listIterator()等所有调用checkForComodification()的地方。

2 如何正确的截断一个List?

subList()返回的是List!是Sublist,而不是原来的类型。

2.1在java.util.AbstractList.subList(int fromIndex, int toIndex)的定义,返回的是java.util.SubList.SubLis:

 public List<E> subList(int fromIndex, int toIndex) {        return (this instanceof RandomAccess ?                new RandomAccessSubList<>(this, fromIndex, toIndex) :                new SubList<>(this, fromIndex, toIndex));    }    声明:    class SubList<E> extends AbstractList<E>{}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

2.2 LinkedList并没有覆盖这个方法.ArryList自己覆盖了这个方法,返回的是java.util.ArrayList.SubList:

    public List<E> subList(int fromIndex, int toIndex) {        subListRangeCheck(fromIndex, toIndex, size);        return new SubList(this, 0, fromIndex, toIndex);    }    声明:    private class SubList extends AbstractList<E> implements RandomAccess {}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

看来ArryList处处体现出RandomAccess接口的特性——支持随机访问。

2.3 我们看一下java.util.AbstractList.clear()方法,这正是我们需要的,Sublist的clear就是这个方法

    public void clear() {        removeRange(0, size());    }
  • 1
  • 2
  • 3

2.3.1 ArrayList的覆盖

    public void clear() {        modCount++;        // clear to let GC do its work        for (int i = 0; i < size; i++)            elementData[i] = null;        size = 0;    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

2.3.2 LinkedList的覆盖

    public void clear() {        for (Node<E> x = first; x != null; ) {            Node<E> next = x.next;            x.item = null;            x.next = null;            x.prev = null;            x = next;        }        first = last = null;        size = 0;        modCount++;    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

3 根据1和2

截短一个List的正确姿势:

list.subList(from, to).clear();
  • 1

总之: 
subList是返回一个镜像而不是新示例 用了 得保证原来的list不能更改。 
之前的抛异常是因为更改了原来的list而要使用sublist的时候必然报异常。 
clear的这个跟这个问题说的是如何获得一个list的某一段顺便释放其他节点。 
这个操作后原来的list会截取出来 类型不变。 
而subList实际上返回的是java.util.Sublist或者java.util.ArrayList.Sublist。

原创粉丝点击