标题
数组和List集合之间的转换
前言:本文介绍数组和list集合之间的转换,通过源码的形式解释各种方法的优劣,通过阅读本篇文章,在实际开发避免步入坑中。
一、数组转List
以此数组为例:
String []str={"abc","def","123"};
1. 方法一、使用for循环将数组元素添加到list集合中
List<String> list=new ArrayList<>(str.length);
for(String str1:str){
list.add(str1);
}
System.out.println(list.toString());//输出[abc, def, 123]
此例采用传统的for循环遍历,可以明显看到,使用for循环遍历,并且且需要一个一个添加到集合中,对于数据量大的数组来说,非常耗时,并不推荐使用。
2. 方法二、使用Arrays.asList()方法(慎用)
list= Arrays.asList(str);
//list.remove(0); 此处将抛出UnsupportedOperationException异常
//list.add("add"); 此处将抛出UnsupportedOperationException异常
System.out.println(list.toString());//输出[abc, def, 123]
str[2]="update"; //修改数组的元素会影响list集合
System.out.println(list.toString());//输出[abc, def, update]
list.set(2,"asList");//修改list集合的元素会影响数组的内容
System.out.println(list.toString());//输出[abc, def, asList]
此方法将数组转换List后,不能对List增删,只能查改,否则将抛出异常,下来我们来看下一源码是如何实现的:
public static <T> List<T> asList(T... a) {
return new ArrayList<>(a);
}
private static class ArrayList<E> extends AbstractList<E>
implements RandomAccess, java.io.Serializable
{
private static final long serialVersionUID = -2764017481108945198L;
private final E[] a;
ArrayList(E[] array) {
a = Objects.requireNonNull(array);
}
//此处省略size() 方法
//此处省略 toArray() 方法
//此处省略 get() 方法
//此处省略 set() 方法
//此处省略 indexOf() 方法
//此处省略 contains() 方法
//此处省略 spliterator() 方法
//此处省略 forEach() 方法
//此处省略 replaceAll() 方法
//此处省略 sort() 方法
}
通过源码我们可以看到Arrays.asList()方法返回是私有静态内部类java.util.Arrays.ArrayList
,并不是java.util.ArrayList类,该内部类继承自AbstractList类,仅有重写set、get方法等,并没有重写add、remove方法,所以很好的解释为什么我们在返回的list中调用add和remove方法抛出异常了。
3. 使用ArrayList的构造方法
List<String> list2=new ArrayList<>(Arrays.asList(str1));
System.out.println(list2.toString());//输出[abc, def, 123]
通过此构造方法我们可以得到一个list集合,仅仅适用于数据量小的时候采用。
4. 使用Collections.addAll() [官方推荐使用的方法]
List<String> list3=new ArrayList<>(str1.length);
Collections.addAll(list3,str1);
System.out.println(list3.toString());//输出[abc, def, 123]
& & Collections.addAll()方法,将数组中的元素转为二进制,然后添加到List中,这是最高效的方法(底层次用的是位运算)。源码如下:
public static <T> boolean addAll(Collection<? super T> c, T... elements) {
boolean result = false;
for (T element : elements)
result |= c.add(element);
return result;
}
5. jdk1.8新增使用Arrays的stream
List<String> list4 = Arrays.stream(str1).collect(Collectors.toList());
System.out.println(list4);//输出[abc, def, 123]
此方法是jdk1.8之后新增的通过stream流的方式来进行转换。
6. jdk1.9使用List.of(),返回的是*【不可变的】*
List<String> list3=List.of(str);
使用前提:当集合中存储的元素个数已经确定,不再改变。此方法为 Java9新增方法,定义在List接口内,并且为静态方法,故可以由类名直接调用。
7. 方式七:使用CollectionUtils.arrayToList()
List<String> list5= CollectionUtils.arrayToList(str1);
System.out.println(list5);
补充一点:当我们使用Arrays.asList()进行转换时,数组的类型引用类型的。
public static <T> List<T> asList(T... a) {
return new ArrayList<>(a);
}
该方法参数是一个泛型T类型的,且返回的类型也是带有泛型的,因此就可以解释,数组的类型为什么必须是引用类型的,因为泛型必须是引用类型
的。
二、List转数组
以此集合为例进行说明
List<String> list=new ArrayList();
list.add("123");
list.add("abc");
list.add("456");
String[] array = new String[list.size()];
1. 方式一、使用for循环得到数组
for(int i = 0; i < list.size();i++){
array[i] = list.get(i);
}
System.out.println(Arrays.toString(array));
2. 方式二、使用带参的toArray()方法
list.toArray(array);//
System.out.println(Arrays.toString(array));
3. 方式三、使用stream流
array= (String[]) list.stream().toArray();
补充一点:使用无参数toArray()方法
array=(String[])list.toArray();
System.out.println(Arrays.toString(array));//抛出ClassCastException异常
使用无参方法返回的是Object类型的,在java中子类的引用不能指向父类或其它子类对象,就算强转也会导致运行失败并抛出ClassCastException;
源码public Object[] toArray() { return Arrays.copyOf(elementData, size); }
如果使用该方法我们可以通过循环的方式将Object类型的数组复制给String类型的数组
Object[] element = list.toArray();
System.out.println(Arrays.toString(element));//输出[123, abc, def]
for (int i = 0; i < element.length; i++) {
array[i] = (String) element[i];
}
System.out.println(Arrays.toString(array));//输出[123, abc, def]