美文网首页
借助可变参数一次创建包含多个元素的 ArrayList

借助可变参数一次创建包含多个元素的 ArrayList

作者: M_lear | 来源:发表于2020-04-09 09:52 被阅读0次

一、开门见山

两种可以直接把要添加元素写在参数列表里的方法:
第一种:Arrays.asList 方式

List<Integer> l1 = new ArrayList<>(Arrays.asList(1, 2, 3));

第二种:Collections.addAll 方式

List<Integer> l2 = new ArrayList<>();
Collections.addAll(l2, 1, 2, 3);

二、抽丝剥茧

ArrayList 自身的构造函数和两个 addAll 方法都不支持可变参数,
public boolean addAll(Collection<? extends E> c)
public boolean addAll(int index, Collection<? extends E> c)

2.1 Arrays.asList 方式

Arrays.asList 实现:

    // asList 方法
    public static <T> List<T> asList(T... a) {
        return new ArrayList<>(a);
    }
    
    // Arrays 内部静态类 ArrayList,和 java.util 包下的 ArrayList 同名,两者没有关系
    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);
        }
        
        @Override
        public int size() {
            return a.length;
        }
        
        @Override
        public Object[] toArray() { // toArray 方法借助 Object 的 clone 方法实现
            return a.clone();
        }
        
        // ...
    }

这里的 ArrayList 是 Arrays 的静态内部类,最大的特点是不能增删元素。
以 asList 可变参数数组为底层存储结构。
其中 toArray 方法通过 Object 的 clone 方法实现:

protected native Object clone() throws CloneNotSupportedException;

ArrayList 参数为 Collection 的构造函数:

    public ArrayList(Collection<? extends E> c) {
        elementData = c.toArray();
        if ((size = elementData.length) != 0) {
            // c.toArray might (incorrectly) not return Object[] (see 6260652)
            if (elementData.getClass() != Object[].class)
                elementData = Arrays.copyOf(elementData, size, Object[].class); // 会执行到这儿
        } else {
            // replace with empty array.
            this.elementData = EMPTY_ELEMENTDATA;
        }
    }

Arrays.copyOf 方法:

    public static <T,U> T[] copyOf(U[] original, int newLength, Class<? extends T[]> newType) {
        @SuppressWarnings("unchecked")
        T[] copy = ((Object)newType == (Object)Object[].class)
            ? (T[]) new Object[newLength]
            : (T[]) Array.newInstance(newType.getComponentType(), newLength);
        System.arraycopy(original, 0, copy, 0,
                         Math.min(original.length, newLength));
        return copy;
    }

Arrays.copyOf()方法详解-jdk1.8
大致要点:
newType 的类型是 Class<? extends T[]>,T 是一个泛型,如果与 Object 做比较的话,父子关系肯定是 Object >= T 的,由于 == 只能在同类型中才能做比较,所以需要强制转换。
class 对象是单例模式,所以可以用 == 比较。
方法目的:数组向上转型并拷贝


总结前面的代码,待添加元素经过两次拷贝后,添加成功。

  • 第一次拷贝发生在,ArrayList 构造函数的第一句 elementData = c.toArray();,通过 Object 的 clone 方法进行了一次拷贝。
  • 第二次拷贝还是发生在 ArrayList 构造函数,elementData = Arrays.copyOf(elementData, size, Object[].class);

解释一下为什么 elementData.getClass() != Object[].class 为 true:
因为执行完 Arrays.asList(1, 2, 3) 后,底层的数组类型为 Integer[]。
所以下面这段代码的执行结果为 true,true。

import java.util.Arrays;

public class Test {
    public static void main(String[] args){
        System.out.println(Arrays.asList(1, 2, 3).toArray().getClass() == Integer[].class);
        // 显式类型声明
        System.out.println(Arrays.<Object>asList(1, 2, 3).toArray().getClass() == Object[].class);
    }
}

2.2 Collections.addAll 方式

    public static <T> boolean addAll(Collection<? super T> c, T... elements) {
        boolean result = false;
        for (T element : elements)
            result |= c.add(element);
        return result;
    }
    
    public boolean add(E e) {
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        elementData[size++] = e;
        return true;
    }

三、结论

初始元素少的话,直接写在可变参数里,喜欢用哪种就用哪种,几个元素的添加不会有什么效率问题。
初始元素多的话,比如添加一个大数组,推荐使用 Arrays.asList 方式,本地方法拷贝会快得多,尽管拷贝两次。

相关文章

  • 借助可变参数一次创建包含多个元素的 ArrayList

    一、开门见山 两种可以直接把要添加元素写在参数列表里的方法:第一种:Arrays.asList 方式 第二种:Co...

  • Java中的集合(二):List

    List是有序且元素可重复集合 ArrayList ArrayList实现了一个可变大小的数组,允许包含所有的元素...

  • 4.3 创建拥有任意数量参数的函数 [Swift教程]

    1. 本节课将为你讲解可变参数的用法 。一个作为可变参数的参数,可以接受零个或多个指定类型的值。 2. 创建可变的...

  • C/C++可变参数

    C/C++中支持传递可变参数,即不定参数,例如: 那么如何拿到不定参数,需要借助va_list类型,包含在cstd...

  • 018-Python之创建单元素tuple

    tuple和list一样,可以包含 0 个、1个和任意多个元素。 包含多个元素的 tuple,前面我们已经创建过了...

  • 17-Python之创建单元素tuple

    tuple和list一样,可以包含 0 个、1个和任意多个元素。 包含多个元素的 tuple,前面我们已经创建过了...

  • 4-8Python之创建单元素tuple

    tuple和list一样,可以包含 0 个、1个和任意多个元素。 包含多个元素的 tuple,前面我们已经创建过了...

  • tuple笔记

    元组就是不可变的列表,列表中除了可变的操作,其他的操作都适用于元组 元组值:使用()将元素包含起来,多个元素之间用...

  • 列表、元组、字典、集合

    简要介绍 元组:包含零个或多任意类型元素,且不可变。用()定义 列表:包含零个或多个任意类型元素,可进行增删改操作...

  • Swift基础篇——函数

    函数的基本的创建方法及使用 有多个返回值的函数 有默认参数值的函数 参数可变的函数一个函数最多只能设置一个可变参数...

网友评论

      本文标题:借助可变参数一次创建包含多个元素的 ArrayList

      本文链接:https://www.haomeiwen.com/subject/rcsqmhtx.html