文章目录
- 一、数组转换成 List 集合
- 二、List 集合转数组
- 浅谈 Arrays.asList
- 1、不能往 Arrays.asList 生成的 List 集合中添加元素
- Arrays.asList 只是简单的充当转换接口
- 二、浅谈 ArrayList 的 subList
- 1、subList 索引的取值边界
- 2、subList 不可强转成 ArrayList
- 3、对 subList 的所有操作都会映射到原列表
一、数组转换成 List 集合
1、使用 java.util
包下的 Arrays.asList
方法进行转换:
import java.util.Arrays;// 转换数组对象
Long[] arr = new Long[]{10L, 20L, 30L};
List<Long> list1 = Arrays.asList(arr);// 转换数组常量
List<String> list2 = Arrays.asList("123", "456", "789");
2、原始类型数组转包装类 List:
long[] arr = new long[]{10L, 20L, 30L};
List<Long> list = Arrays.stream(arr).boxed().collect(Collectors.toList());
二、List 集合转数组
1、使用 List 集合的 toArray
方法:
// 方式1(推荐)
List<Long> list = Arrays.asList(10L, 20L, 30L);
Long[] array = list.toArray(new Long[0]);// 方式2
List<String> list = Arrays.asList("123", "456", "789");
String[] array = (String[]) list.toArray();
2、使用 Java8 流式编程方式:
// 方式1(推荐)
List<Long> list = Arrays.asList(10L, 20L, 30L);
Long[] array = list.stream().map(Long::valueOf).toArray(Long[]::new);// 方式2
List<String> list = Arrays.asList("123", "456", "789");
Long[] array = (Long[]) list.stream().map(Long::valueOf).toArray();
如果转换的数组类型为原始基本类型(double[]
, long[]
, int[]
)的话,可以使用另一个映射方法(这几个方法已经帮我们转换成了原始基本类型):
double[] doubles = list.stream().mapToDouble(Double::new).toArray();
long[] longs = list.stream().mapToLong(Long::new).toArray();
int[] ints = list.stream().mapToInt(Integer::new).toArray();
3、使用第三方工具,比如 Apache 的 commons-beanutils
工具包:
<!-- 引入依赖 -->
<dependency><groupId>commons-beanutils</groupId><artifactId>commons-beanutils</artifactId><version>1.9.4</version>
</dependency>
import org.apache.commons.beanutils.ConvertUtils;List<String> list = Arrays.asList("123", "456", "789");
String[] array = (String[]) ConvertUtils.convert(list, String[].class);
总结:使用第三方工具包的方式通过性比较强,它可以转换任何类型,但使用 List 集合的 toArray
方法和 Java8 的流式编程比较简单方便,不需要引入其它依赖,所以比较推荐。
浅谈 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.UnsupportedOperationExceptionat 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
类,但它只实现了 get
、set
、contain
、indexOf
等方法,却没有实现 add
、remove
、clear
等方法来支持添加、删除操作。
3)总结:
当我们使用 Arrays.asList
把数组转换成集合时,不能使用其修改集合的相关方法,一般只是适用于遍历访问操作。
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.ConcurrentModificationExceptionat 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
异常,而结构性修改子集合时,会影响原集合,但不会报异常。