subList和asList

简介: subList subList返回仅仅只是一个视图。直接上源码 public List subList(int fromIndex, int toIndex) { subListRangeCheck(fromIndex, toIndex, size); r...

subList

subList返回仅仅只是一个视图。直接上源码

public List<E> subList(int fromIndex, int toIndex) {
        subListRangeCheck(fromIndex, toIndex, size);
        return new SubList(this, 0, fromIndex, toIndex);
    }

subListRangeCheck方式是判断fromIndex、toIndex是否合法,如果合法就直接返回一个subList对象,注意在产生该new该对象的时候传递了一个参数 this ,该参数非常重要,因为他代表着原始list。

/**
     * 继承AbstractList类,实现RandomAccess接口
     */
    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;
        }

        //set方法
        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;
        }

        //get方法
        public E get(int index) {
            rangeCheck(index);
            checkForComodification();
            return ArrayList.this.elementData(offset + index);
        }

        //add方法
        public void add(int index, E e) {
            rangeCheckForAdd(index);
            checkForComodification();
            parent.add(parentOffset + index, e);
            this.modCount = parent.modCount;
            this.size++;
        }

        //remove方法
        public E remove(int index) {
            rangeCheck(index);
            checkForComodification();
            E result = parent.remove(parentOffset + index);
            this.modCount = parent.modCount;
            this.size--;
            return result;
        }
    }

该SubLsit是ArrayList的内部类,它与ArrayList一样,都是继承AbstractList和实现RandomAccess接口。同时也提供了get、set、add、remove等list常用的方法。但是它的构造函数有点特殊,在该构造函数中有两个地方需要注意:

1、this.parent = parent;而parent就是在前面传递过来的list,也就是说this.parent就是原始list的引用

2、this.offset = offset + fromIndex;this.parentOffset = fromIndex;。同时在构造函数中它甚至将modCount(fail-fast机制)传递过来了。

我们再看get方法,在get方法中return ArrayList.this.elementData(offset + index);这段代码可以清晰表明get所返回就是原列表offset + index位置的元素。同样的道理还有add方法里面的:

parent.add(parentOffset + index, e);
this.modCount = parent.modCount;

remove方法里面的

E result = parent.remove(parentOffset + index);
this.modCount = parent.modCount;

诚然,到了这里我们可以判断subList返回的SubList同样也是AbstractList的子类,同时它的方法如get、set、add、remove等都是在原列表上面做操作,它并没有像subString一样生成一个新的对象。所以subList返回的只是原列表的一个视图,它所有的操作最终都会作用在原列表上

从上面我们知道subList生成的子列表只是原列表的一个视图而已,如果我们操作子列表它产生的作用都会在原列表上面表现,但是如果我们操作原列表会产生什么情况呢?

public static void main(String[] args) {
        List<Integer> list1 = new ArrayList<Integer>();
        list1.add(1);
        list1.add(2);
        
        //通过subList生成一个与list1一样的列表 list3
        List<Integer> list3 = list1.subList(0, list1.size());
        //修改list3
        list1.add(3);
        
        System.out.println("list1'size:" + list1.size());
        System.out.println("list3'size:" + list3.size());//子视图里有modcount比较校验
    }

该实例如果不产生意外,那么他们两个list的大小都应该都是3,但是偏偏事与愿违,事实上我们得到的结果是这样的:

list1'size:3
Exception in thread "main" java.util.ConcurrentModificationException
    at java.util.ArrayList$SubList.checkForComodification(Unknown Source)
    at java.util.ArrayList$SubList.size(Unknown Source)
    at com.chenssy.test.arrayList.SubListTest.main(SubListTest.java:17)

list1正常输出,但是list3就抛出ConcurrentModificationException异常。我们再看size方法:

public int size() {
            checkForComodification();
            return this.size;
        }

size方法首先会通过checkForComodification验证,然后再返回this.size。

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

该方法表明当原列表的modCount与this.modCount不相等时就会抛出ConcurrentModificationException。同时我们知道modCount 在new的过程中 "继承"了原列表modCount,只有在修改该列表(子列表)时才会修改该值(先表现在原列表后作用于子列表)。而在该实例中我们是操作原列表,原列表的modCount当然不会反应在子列表的modCount上啦,所以才会抛出该异常。

对于子列表视图,它是动态生成的,生成之后就不要操作原列表了,否则必然都导致视图的不稳定而抛出异常。最好的办法就是将原列表设置为只读状态,要操作就操作子列表

//通过subList生成一个与list1一样的列表 list3
List<Integer> list3 = list1.subList(0, list1.size());
        
//对list1设置为只读状态
list1 = Collections.unmodifiableList(list1);

 

在开发过程中我们一定会遇到这样一个问题:获取一堆数据后,需要删除某段数据。例如,有一个列表存在1000条记录,我们需要删除100-200位置处的数据,我们可以这样处理:

子列表的操作都会反映在原列表上。所以下面一行代码全部搞定:

list1.subList(100, 200).clear();

asList

1.在使用asList时不要将基本数据类型当做参数。

2.asList返回的列表只不过是一个披着list的外衣(Arrays里面的内部类ArrayList),它并没有list的基本特性(变长)。该list是一个长度不可变的列表,传入参数的数组有多长,其返回的列表就只能是多长。

 

//入参需要是泛型
public
static <T> List<T> asList(T... a) { return new ArrayList<>(a); }
//基本类型不能当泛型传入,而数组可以,所以个数是一个,类型是数组 public static void main(String[] args) { int[] ints = {1,2,3,4,5}; List list = Arrays.asList(ints); System.out.println("list'size:" + list.size()); System.out.println("list 的类型:" + list.get(0).getClass()); System.out.println("list.get(0) == ints:" + list.get(0).equals(ints)); } -------------------------------------------- outPut: list'size:1 list 的类型:class [I list.get(0) == ints:true

//正确方式,Integer可以是泛型 public static void main(String[] args) { Integer[] ints = {1,2,3,4,5}; List list = Arrays.asList(ints); System.out.println("list'size:" + list.size()); System.out.println("list.get(0) 的类型:" + list.get(0).getClass()); System.out.println("list.get(0) == ints[0]:" + list.get(0).equals(ints[0])); } ---------------------------------------- outPut: list'size:5 list.get(0) 的类型:class java.lang.Integer list.get(0) == ints[0]:true

使用工具类 Arrays.asList()把数组转换成集合时,不能使用其修改集合相关的方 法,它的 add/remove/clear 方法会抛出 UnsupportedOperationException 异常。 说明:asList 的返回对象是一个 Arrays 内部类,并没有实现集合的修改方法。Arrays.asList 体现的是适配器模式,只是转换接口,后台的数据仍是数组。

   String[] str = new String[] { "a", "b" };
   List list = Arrays.asList(str);

第一种情况:list.add("c"); 运行时异常。

第二种情况:str[0]= "jinyong"; 那么 list.get(0)也会随之修改 

 

目录
相关文章
|
Java
ArrayList与LinkedList的遍历删除元素方法
ArrayList与LinkedList的遍历删除元素方法
319 0
集合排序-List集合
在Collections下有一个方法,sort()方法,不仅是数值,其他类型也有默认的排序方法
|
缓存 安全 算法
ArrayList 可以完全替代数组吗?
在前面的文章里],我们学习了很多数据结构与算法思想。在实际的业务开发中,往往不需要我们手写数据结构,而是直接使用标准库的数据结构 / 容器类。 在后续的文章里,我们将以 Java 语言为例,分析从 ArrayList 到 LinkedHashMap 等一系列标准库容器类,最后再有一篇总结回顾,请关注。
136 0
|
运维 Java 大数据
ArrayList和SubList的坑面试题
ArrayList和SubList的坑面试题
8709 7
ArrayList和SubList的坑面试题
List集合与Set集合(ArrayList,LinkedList,Vector,HashSet,LinkedHashSet,可变参数)
List集合介绍及常用方法 import java.util.ArrayList; import java.util.Iterator; import java.util.List; /* java.util.List接口 extends Collection接口 List接口的特点: 1.有序的集
List集合与Set集合(ArrayList,LinkedList,Vector,HashSet,LinkedHashSet,可变参数)
|
安全
比较ArrayList、LinkedList、Vector
比较ArrayList、LinkedList、Vector
115 0
比较ArrayList、LinkedList、Vector
|
安全 C++
HashSet VS TreeSet
不同之处 像add,remove,contains,size等操作,HashSet比TreeSet有更好的性能。HashSet的时间复杂度为O(1),TreeSet的时间复杂度为log(n)。