Java中数组和List互相转换(深入源码级别的探究各个方法优劣)

文章目录

  1. 数组转换为List
    1. 基本数据类型数组转换为List
    2. 对象类型数组转换为List
  2. java.util.Arrays.asList()方法的使用注意事项
  3. List转换为数组
    1. List转换为Object类型的数组
    2. List转换为指定类型的数组
  4. 参考文章

一、数组转换为List

这里我把数组分为两种类型,基本数据类型数组和对象类型的数组。这两种数组在转换为List时有有所区别。

(一)基本数据类型数组转换为List

下面所列举的例子都以int类型的数组为例

1.遍历转换

public static List<Integer> arrayToListUseFor(int[] a){
    ArrayList<Integer> list = new ArrayList<>();

    for (int e : a) {
        list.add(e);
    }

    return list;
}

// main方法
public static void main(String[] args) {
    int[] a = {1, 2, 3};
    List<Integer> list = arrayToListUseFor(a);

    System.out.println(list);
    String name = list.getClass().getName();
    System.out.println(name);
}

输出结果

[1, 2, 3]
java.util.ArrayList

注意:这种方式是线程不安全的,不要在多线程环境下使用它

2.基于JDK8的函数式编程

这种方式需要JDK的版本是1.8及以上

public static List<Integer> arrayToListUseFunctional(int [] a){
    return Arrays
        .stream(a)
        .boxed()
        .collect(Collectors.toList());
}

// main方法
public static void main(String[] args) {
    int[] a = {1, 2, 3};
    List<Integer> list = arrayToListUseFunctional(a);

    System.out.println(list);
    String name = list.getClass().getName();
    System.out.println(name);
}

输出结果

[1, 2, 3]
java.util.ArrayList

这种方式对于基本数据类型的数组和对象类型的数组都是适用的

3.使用org.springframework.util.CollectionUtils.arrayToList()方法

这种方法需要Spring框架的支持

public static List arrayToListUseCollectionUtils(int[] a){
    return CollectionUtils.arrayToList(a);
}

// main方法
public static void main(String[] args) {
    int[] a = {1, 2, 3};
    List list = arrayToListUseCollectionUtils(a);

    for (Object o : list) {
        System.out.println(o);
    }

    String className = list.getClass().getName();
    System.out.println(className);
}

输出结果

1
2
3
java.util.Arrays$ArrayList

注意:CollectionUtils.arrayToList()方法实际上使用的是java.util.Arrays.asList()这个方法

arrayToList()的源码

@SuppressWarnings("rawtypes")
public static List arrayToList(Object source) {
    return Arrays.asList(ObjectUtils.toObjectArray(source));
}

这个方法返回值的类型是java.util.Arrays.ArrayList,而非是java.util.ArrayList。关于Arrays.asList()的使用注意事项参考文章第二节——Arrays.asList()使用注意事项

(二)对象类型数组转换为List

1.遍历转换

方式同基本数据类型数组转List一样

2.使用java.util.Collections.addAll()方法

public static List<String> arrayToListUseCollections(String[] a){
    List<String> list = new ArrayList<>();
    Collections.addAll(list, a);
    return list;
}

// main方法
public static void main(String[] args) {
    String[] a = {"a", "b", "c"};
    List<String> list = arrayToListUseCollections(a);

    System.out.println(list);
    String name = list.getClass().getName();
    System.out.println(name);
}

输出结果

[a, b, c]
java.util.ArrayList

注意:这种方式使用的是增强for循环进行遍历添加

3.使用java.util.Arrays.asList()方法

public static List<String> arrayToListUseAsList(String[] a){
    return Arrays.asList(a);
}

// main方法
public static void main(String[] args) {
    String[] a = {"a", "b", "c"};
    List<String> list = arrayToListUseAsList(a);

    System.out.println(list);
    String name = list.getClass().getName();
    System.out.println(name);
}

输出结果

[a, b, c]
java.util.Arrays$ArrayList

关于Arrays.asList()的使用注意事项参考文章的第二节

4.将Arrays.asList()的返回值作为ArrayList()的构造参数

public static List<String> arrayToListUseNewArrayList(String[] a){
    List<String> resource = Arrays.asList(a);
    List<String> list = new ArrayList<>(resource);
    return list;
}

// main方法
public static void main(String[] args) {
    String[] a = {"a", "b", "c"};
    List<String> list = arrayToListUseNewArrayList(a);

    System.out.println(list);
    String name = list.getClass().getName();
    System.out.println(name);
}

输出结果

[a, b, c]
java.util.ArrayList

二、java.util.Arrays.asList()方法的使用注意事项

参考阿里巴巴Java开发手册

【强制】使用工具类Arrays.asList()把数组转换成集合时,不能使用其修改集合相关的方法,它的add/remove/clear方法会抛出UnsupportedOperationException异常。
说明:asList 的返回对象是一个 Arrays 内部类,并没有实现集合的修改方法。Arrays.asList
体现的是适配器模式,只是转换接口,后台的数据仍是数组。
String[] str = new String[] { "you", "wu" };
List list = Arrays.asList(str);
第一种情况:list.add("yangguanbao"); 运行时异常。
第二种情况:str[0] = "gujin"; 那么 list.get(0)也会随之修改。

具体原因的探究和其他注意事项参考文章

https://blog.csdn.net/u010870518/article/details/91376347


三、List转换为数组

(一)List转换为Object数组

示例代码如下

public static <T> Object[] getObjectArray(List<T> list){
    Object[] objects = list.toArray();
    return objects;
}

// main方法
public static void main(String[] args) {
    List<String> list = new ArrayList<>();
    list.add("a");
    list.add("b");
    list.add("c");

    Object[] objectArray = getObjectArray(list);

    for (Object o : objectArray) {
        System.out.println(o);
    }
}

输出结果

a
b
c

toArray()是List接口提供的一个方法,这里我们来看看ArrayList对这个方法的实现

public Object[] toArray() {
    return Arrays.copyOf(elementData, size);
}

可以看到,ArrayList对该方法的实现是把自己内部维护的Object数组——elementData中的所有元素复制到一个新的数组里,然后再将这个新的数组返回。新的数组中存放的是List原始对象的引用,并没有对List中原始对象进行拷贝或复制。

值得一提的是,Arrays.copyOf()这个方法底层使用的是System.arraycopy()这个本地方法(使用native关键字修饰),ArrayList扩容时也是使用的这个方法将旧数组的所有元素复制到扩容后的数组里。

注意:System.arraycopy()拷贝方式属于浅拷贝。也即是说,如果List的泛型是自定义对象,在使用toArray()方法把List转换为数组后,再对原来的List的内容进行修改的话,数组里的内容也会对应的被更改。下面通过一个例子来证明。

  1. 创建一个用于测试的自定义对象People
public class People {
    private String name;

    private Integer age;

    // get、set、Constructor、toString
}
  1. 写一个测试方法
public static void main(String[] args) {
    // 创建两个People对象
    People p1 = new People("小明", 10);
    People p2 = new People("小王", 10);

    // 创建一个ArrayList,放入上面两个People对象
    List<People> list = new ArrayList<>(2);
    list.add(p1);
    list.add(p2);

    // 将ArrayList转换为数组并打印出数组里的内容
    Object[] objectArray = getObjectArray(list);
    System.out.println(Arrays.toString(objectArray));

    // 修改ArrayList中第一个元素p1的内容
    People people1 = list.get(0);
    people1.setName("长大的小明");
    people1.setAge(20);

    // 再次打印数组的内容
    System.out.println("----------修改ArrayList中第一个元素p1的内容后----------");
    System.out.println(Arrays.toString(objectArray));
}

输出结果

[People{name='小明', age=10}, People{name='小王', age=10}]
----------修改List中的内容后----------
[People{name='长大的小明', age=20}, People{name='小王', age=10}]

通过程序的输出结果可以很明显的看到:我们并没有修改数组中的数据,只是修改了原始的ArrayList的数据,结果导致了数组中的数据也跟着发生了变化。所以平时写代码时要注意这一点,避免踩坑!

(二)List转换为指定类型数组

这种方式可以获得一个指定类型的数组,不再是一个Object类型的数组。

示例代码如下

public static <T> T[] getGenericArray(List<T> list, Class<T> clazz){
    // 获得一个泛型数组
    T[] array = (T[]) Array.newInstance(clazz, list.size());
    // 传入指定类型的数组。这个方法的返回值还是传进去的数组,接不接收都一样
    list.toArray(array);
    return array;
}

// main方法
public static void main(String[] args) {
    List<Integer> list = new ArrayList<>();
    list.add(1);
    list.add(2);
    list.add(3);

    Integer[] genericArray = getGenericArray(list, Integer.class);
    System.out.println(Arrays.toString(genericArray));
}

输出结果

[1, 2, 3]

同样的,ArrayList对此方法的实现同样使用的是System.arraycopy()方法,所以,这个方法的拷贝方式也是浅拷贝


参考文章

千万不要这样使用Arrays.asList !
Java中List与数组互相转换
深入理解List的toArray()方法和toArray(T[] a)方法

原文地址:https://www.cnblogs.com/lmw97/p/12922351.html