【Java基础】之 Arrays.asList 和 ArrayList 的 subList

目录

  • 一、浅谈 Arrays.asList
    • 1、不能往 Arrays.asList 生成的 List 集合中添加元素
    • 2、Arrays.asList 只是简单的充当转换接口
  • 二、浅谈 ArrayList 的 subList
    • 1、subList 索引的取值边界
    • 2、subList 不可强转成 ArrayList
    • 3、对 subList 的所有操作都会映射到原列表

一、浅谈 Arrays.asList


1、不能往 Arrays.asList 生成的 List 集合中添加元素

1)场景

Arrays.asList() 方法很方便地为我们快捷创建一个 List 集合,比如:

List<String> list = Arrays.asList("123", "456", "789");

但是,如果我们往生成的 list 添加元素时:

list.add("2233");

会抛出 java.lang.UnsupportedOperationException 异常:

java.lang.UnsupportedOperationException
	at java.util.AbstractList.add(AbstractList.java:148)
	at java.util.AbstractList.add(AbstractList.java:108)

也就是说,使用 Arrays.asList 生成的 List 集合不允许添加元素。

2)原因

要找出原因,我们需要回到源码当中,进入 atList 源码:

// atList 方法源码
public static <T> List<T> asList(T... a) {
    return new ArrayList<>(a);
}

// Arrays 的静态内部类 ArrayList
private static class ArrayList<E> extends AbstractList<E>
    implements RandomAccess, java.io.Serializable
{
    private static final long serialVersionUID = -2764017481108945198L;
    private final E[] a;
    ......
}

从源码中可以看出,Arrays.asList 返回的是 Arrays 类中的静态内部类 ArrayList 对象,并非是 java.util 包下的 ArrayList 类对象。

虽然这个静态内部类 ArrayList 同样是继承了 AbstractList 类,但它只实现了 getsetcontainindexOf 等方法,却没有实现 addremoveclear 等方法来支持添加、删除操作。

3)总结

当我们使用 Arrays.asList 把数组转换成集合时,不能使用其修改集合的相关方法,一般只是适用于遍历访问操作。

2、Arrays.asList 只是简单的充当转换接口

Arrays.asList 体现的是适配器模式,只是转换接口,后台的数据仍然是数组,如果修改了原数组数据,其生产的 List 集合数据也会跟着一起被修改:

// 原数组数据
String[] strs = new String[]{"123", "456", "789"};

// 生成 List 集合
List<String> list = Arrays.asList(strs);
System.out.println(list.toString());

// 修改原数组数据
strs[0] = "666";
System.out.println(list.toString());

>>>>>>
[123, 456, 789]
[666, 456, 789]

二、浅谈 ArrayList 的 subList


1、subList 索引的取值边界

下面是一个简单的 subList 示例:

List<String> list = new ArrayList<>();
list.add("111");
list.add("222");
list.add("333");
List<String> subList = list.subList(0, 2);
System.out.println(subList);

>>>>
[111, 222]

可以看出,subList 的索引是从 fromIndex(包含)到 toIndex(不包含)。

2、subList 不可强转成 ArrayList

我们进去看一下 ArrayList 的 subList 方法源码:

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

// ArrayList 内部类 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 返回的是 ArrayList 的成员内部类 SubList,如果强转成 ArrayList 的话会抛出异常:java.lang.ClassCastException: java.util.ArrayList$SubList cannot be cast to java.util.ArrayList

3、对 subList 的所有操作都会映射到原列表

1)非结构性修改会相互影响

究其原因,subList 返回的内部类 SubList 是 ArrayList 的一个视图,对 SubList 子列表的所有操作最终都会映射到原列表上(对原列表的操作也会影响子列表),如下示例:

// 创建父子列表
List<String> list = new ArrayList<>();
list.add("111");
list.add("222");
list.add("333");
List<String> subList = list.subList(0, 2);
System.out.println(list);
System.out.println(subList);

// 修改原列表
list.set(0, "666");
System.out.println(list);
System.out.println(subList);

// 修改子列表
subList.set(0, "888");
System.out.println(list);
System.out.println(subList);

>>>>>
[111, 222, 333]
[111, 222]
---------------
[666, 222, 333]
[666, 222]
---------------
[888, 222, 333]
[888, 222]

2)对父集合的增加或删除(结构性修改),都会导致子列表的遍历、增加、删除产生 ConcurrentModificationExcetption 异常

List<String> list = new ArrayList<>();
list.add("111");
list.add("222");
list.add("333");
List<String> subList = list.subList(0, 2);

// 原列表添加元素
list.add("666");

System.out.println(list);		// 遍历父列表
System.out.println(subList);	// 遍历子列表

>>>>
[111, 222, 333, 666]
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)

从上面可以看出,对原集合进行添加操作(结构性修改),会导致遍历子集合时引起ConcurrentModificationExcetption 异常。

注意:ConcurrentModificationExcetption 异常并不是在添加元素时发生的,而是在添加元素后,遍历子集合时发生的。

3)对子列表结构性修改会影响原列表但不会触发异常

List<String> list = new ArrayList<>();
list.add("111");
list.add("222");
list.add("333");
List<String> subList = list.subList(0, 2);

// 子列表添加元素
subList.add("666");

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

>>>>>
[111, 222, 666, 333]
[111, 222, 666]

从上面可以看出,对子列表的结构性修改会影响到父列表,但遍历的时候不会引起异常。

4)总结

ArrayList 的 subList 方法,返回的是原集合的一个子集合(视图),非结构性修改任意一个集合的元素的值,都会彼此影响;结构性修改原集合时,会报 ConcurrentModificationException 异常,而结构性修改子集合时,会影响原集合,但不会报异常。

你可能感兴趣的:(Java,java)