Java中数组转集合总结

2022-03-29 14:59:04 浏览数 (1)

一、使用Arrays.asList() 方法

代码语言:javascript复制
package com.joshua317;

import java.util.*;

public class Main {

    public static void main(String[] args) {
        String[] namesArr = {"joshua317","joshua318","joshua319"};
        List<String> namesLst = Arrays.asList(namesArr);
        for (String name: namesLst) {
            System.out.println(name);
        }
    }
}

对于Arrays.asList()方法需要注意以下几点: 1.该方法返回的是基于数组的List视图(List view)。所以,这种方式是将数组转换为List的最快的方式。因为返回的只是视图,不需要多余的内存来创建新的List以及复制操作。

2.该方法返回的List是长度是固定的(fixed),不是只读的。所以我们不能进行删除、添加操作,而可以使用set()方法进行修改元素操作。如果你对返回的List执行add()添加新元素,会返回UnsupportedOperationException。

代码语言:javascript复制
package com.joshua317;

import java.util.*;

public class Main {

    public static void main(String[] args) {
        String[] namesArr = {"joshua317","joshua318","joshua319"};
        List<String> namesLst = Arrays.asList(namesArr);
        namesLst.add("joshua319");
        for (String name: namesLst) {
            System.out.println(name);
        }
    }
}

3.因为该方法返回的是基于原数组的List视图,所以,当我们使用set方法修改了List中的元素的时候,那么原来的数组也会跟着改变(这是视图的特性)。

代码语言:javascript复制
package com.joshua317;

import java.util.*;

public class Main {

    public static void main(String[] args) {
        String[] namesArr = {"joshua317","joshua318","joshua319"};
        List<String> namesLst = Arrays.asList(namesArr);
        namesLst.set(0,"joshua320");
        System.out.println("==========list==========");
        for (String name: namesLst) {
            System.out.println(name);
        }
        System.out.println("==========array==========");
        for (String name: namesArr) {
            System.out.println(name);
        }
    }
}

4.从java 5开始,该方法支持泛型,所以我们可以从数组中得到类型安全ArrayList。

注意:

1.如果我们想让转换为只读的List,可以使用Collections.unmodifiableList()方法来将数组转换为指定List。

代码语言:javascript复制
package com.joshua317;

import java.util.*;

public class Main {

    public static void main(String[] args) {
        String[] namesArr = {"joshua317","joshua318","joshua319"};
        final List<String> namesLst = Collections.unmodifiableList(Arrays.asList(namesArr));

        for (String name: namesLst) {
            System.out.println(name);
        }
    }
}

2.如果想返回的方法能够进行添加、删除元素操作,则可以使用new ArrayList(Arrays.asList(array)) ,这样就会创建一个对象类型的ArrayList,并将数组的内容拷贝过去。

代码语言:javascript复制
package com.joshua317;

import java.util.*;

public class Main {

    public static void main(String[] args) {
        String[] namesArr = {"joshua317","joshua318","joshua319"};
        ArrayList<String> namesLst = new ArrayList<>(Arrays.asList(namesArr));
        namesLst.add("joshua320");

        for (String name: namesLst) {
            System.out.println(name);
        }
    }
}

二、使用Collections.addAll()方法

使用Collections.addAll()方法没有第一种方法高效,但是更加灵活。同样也是新建一个ArrayList,将数组的内容复制进去。

代码语言:javascript复制
package com.joshua317;

import java.util.*;

public class Main {

    public static void main(String[] args) {
        String[] namesArr = {"joshua317","joshua318","joshua319"};
        ArrayList<String> namesLst = new ArrayList<>();

        Collections.addAll(namesLst, namesArr);
        namesLst.add("joshua320");

        for (String name: namesLst) {
            System.out.println(name);
        }
    }
}

对于Collections.addAll()方法:

1.没有Arrays.asList()快,但是更加灵活。

2.该方法实际上是将数组的内容复制到ArrayList中

3.因为是复制内容到ArrayList中,所以我们对ArrayList进行修改、添加、删除操作都不会影响原来的数组。

4.该方法相当于一个添加操作。该方法并不会覆盖ArrayList中已经存在的元素。

代码语言:javascript复制
package com.joshua317;

import java.util.*;

public class Main {

    public static void main(String[] args) {
        String[] namesArr = {"joshua317","joshua318","joshua319"};
        ArrayList<String> namesLst = new ArrayList<>();

        Collections.addAll(namesLst, namesArr);
        namesLst.add("---------");
        Collections.addAll(namesLst, namesArr);

        for (String name: namesLst) {
            System.out.println(name);
        }
    }
}

三、使用ArrayList的构造方法

其实上面方法一中已经提到了,使用ArrayList的构造方法同时结合了Arrays.asList方法

代码语言:javascript复制
package com.joshua317;

import java.util.*;

public class Main {

    public static void main(String[] args) {
        String[] namesArr = {"joshua317","joshua318","joshua319"};
        ArrayList<String> namesLst = new ArrayList<>(Arrays.asList(namesArr));
        namesLst.add("joshua320");

        for (String name: namesLst) {
            System.out.println(name);
        }
    }
}

ArrayList构造方法:

ArrayList(Collection < ? extends E > c) : 构造一个包含特定容器的元素的列表,并且根据容器迭代器的顺序返回。 所以构造方法所做的事情如下:

1.将容器c转换为一个数组

2.将数组拷贝到ArrayList中称为”elementData”的数组中

ArrayList的构造方法的源码如下:

代码语言:javascript复制
  public ArrayList(Collection<? extends E> c) {
        Object[] a = c.toArray();
        if ((size = a.length) != 0) {
            if (c.getClass() == ArrayList.class) {
                elementData = a;
            } else {
                elementData = Arrays.copyOf(a, size, Object[].class);
            }
        } else {
            // replace with empty array.
            elementData = EMPTY_ELEMENTDATA;
        }
    }

四、使用ArrayList的addAll()方法

ArrayList的addAll()方法结合Arrays.asList方法使用

代码语言:javascript复制
package com.joshua317;

import java.util.*;

public class Main {

    public static void main(String[] args) {
        String[] namesArr = {"joshua317","joshua318","joshua319"};
        ArrayList<String> namesLst = new ArrayList<>();

        namesLst.addAll(Arrays.asList(namesArr));
        namesLst.add("joshua320");

        for (String name: namesLst) {
            System.out.println(name);
        }
    }
}

ArrayList.addAll(Collection < ? extends E > c) : 构造一个包含特定容器的元素的列表,并且根据容器迭代器的顺序返回。基本上和构造方法的原理一样。 所以addAll方法所做的事情如下:

1.将容器c转换为一个数组

2.将数组拷贝到ArrayList中称为”elementData”的数组中

ArrayList的addAll方法的源码如下:

代码语言:javascript复制
  public boolean addAll(Collection<? extends E> c) {
        Object[] a = c.toArray();
        int numNew = a.length;
        ensureCapacityInternal(size   numNew);  // Increments modCount
        System.arraycopy(a, 0, elementData, size, numNew);
        size  = numNew;
        return numNew != 0;
    }

五、拓展

5.1 为何对Arrays.asList()返回的List进行添加、删除操作会报错,而set方法却可以使用?

来看下Arrays.asList()方法相关源代码

代码语言:javascript复制
 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);
        }

        @Override
        public int size() {
            return a.length;
        }

        @Override
        public Object[] toArray() {
            return a.clone();
        }

        @Override
        @SuppressWarnings("unchecked")
        public <T> T[] toArray(T[] a) {
            int size = size();
            if (a.length < size)
                return Arrays.copyOf(this.a, size,
                                     (Class<? extends T[]>) a.getClass());
            System.arraycopy(this.a, 0, a, 0, size);
            if (a.length > size)
                a[size] = null;
            return a;
        }

        @Override
        public E get(int index) {
            return a[index];
        }

        @Override
        public E set(int index, E element) {
            E oldValue = a[index];
            a[index] = element;
            return oldValue;
        }

        @Override
        public int indexOf(Object o) {
            E[] a = this.a;
            if (o == null) {
                for (int i = 0; i < a.length; i  )
                    if (a[i] == null)
                        return i;
            } else {
                for (int i = 0; i < a.length; i  )
                    if (o.equals(a[i]))
                        return i;
            }
            return -1;
        }

        @Override
        public boolean contains(Object o) {
            return indexOf(o) != -1;
        }

        @Override
        public Spliterator<E> spliterator() {
            return Spliterators.spliterator(a, Spliterator.ORDERED);
        }

        @Override
        public void forEach(Consumer<? super E> action) {
            Objects.requireNonNull(action);
            for (E e : a) {
                action.accept(e);
            }
        }

        @Override
        public void replaceAll(UnaryOperator<E> operator) {
            Objects.requireNonNull(operator);
            E[] a = this.a;
            for (int i = 0; i < a.length; i  ) {
                a[i] = operator.apply(a[i]);
            }
        }

        @Override
        public void sort(Comparator<? super E> c) {
            Arrays.sort(a, c);
        }
    }

根据源码我们可以得知,Arrays.asList()方法返回的是个内部的ArrayList,这个类同样是AbstractList的一种实现。而该ArrayList就只有以下方法,并没有实现add和remove方法:

代码语言:javascript复制
contain(Object)
get(int)
indexOf(Object)
set(int)
size()
toArray()
toArray(T[])

根本就没有add()和remove()方法。这个类由于是AbstractList的一种实现,AbstractList的add和remove方法会有异常抛出:

代码语言:javascript复制
 /**
     * Appends the specified element to the end of this list (optional
     * operation).
     *
     * <p>Lists that support this operation may place limitations on what
     * elements may be added to this list.  In particular, some
     * lists will refuse to add null elements, and others will impose
     * restrictions on the type of elements that may be added.  List
     * classes should clearly specify in their documentation any restrictions
     * on what elements may be added.
     *
     * <p>This implementation calls {@code add(size(), e)}.
     *
     * <p>Note that this implementation throws an
     * {@code UnsupportedOperationException} unless
     * {@link #add(int, Object) add(int, E)} is overridden.
     *
     * @param e element to be appended to this list
     * @return {@code true} (as specified by {@link Collection#add})
     * @throws UnsupportedOperationException if the {@code add} operation
     *         is not supported by this list
     * @throws ClassCastException if the class of the specified element
     *         prevents it from being added to this list
     * @throws NullPointerException if the specified element is null and this
     *         list does not permit null elements
     * @throws IllegalArgumentException if some property of this element
     *         prevents it from being added to this list
     */
    public boolean add(E e) {
        add(size(), e);
        return true;
    }

所以 当我们对返回的List执行add和remove方法时,就会报UnsupportedOperationException了。但是因为有set()方法,所以,我们可以修改返回的List。

5.2 我们说Arrays.asList()返回的是基于原数组的List视图, 而且修改List的元素时候,原数组的内容也会同时改变,这又是为何呢?

根据上面的代码,我们可以知道, 我们调用返回的ArrayList的set(),get(), indexOf(), contain(),size()这些方法,本质上都是去对原数组进行对应的操作。所以,我们改变返回的ArrayList中的内容的时候,原数组也会同时改变。这就是集合视图(collection view),集合了常用的方法。

5.3 为何返回的ArrayList的长度是固定的?还有为什么Arrays.asList()方法最快?

还是上面的代码,一般来说,ArrayList内部有一个对象类型数组作为实例变量来存放ArrayList中的数据。而上面的内部类中,ArrayList的这个实例变量就是a,而它只是将引用指向了原数组,并未将原数组的内容复制到a中。这样就没有进行复制操作,也没有创建新的数组对象,自然最快了。

同时,该内部类ArrayList并为提供add方法等方法,自然是无法修改ArrayList的长度。而且因为是直接将实例变量a指向原数组,我们知道数组一旦初始化后就没法修改它的大小了,所以原数组不能改变大小,自然返回的ArrayList的长度也不能改变长度,长度就只能是固定的。

本文为joshua317原创文章,转载请注明:转载自joshua317博客 https://www.joshua317.com/article/243

0 人点赞