深入解析 Java 数组 Arrays.copyOf 、Arrays.asList

前言

java 数组定义和 C 语言中有些区别, Arrays 类提供了一些工具方法,本文总结了copyOf()asList()使用过程中的一些注意点。

本文链接 http://www.alijava.com/array-copy/ 转载请注明出处

数组基础

定义数组

1
2
int[] a1 = { 1, 2, 3 }; //Java 风格
int a1[] = { 1, 2, 3 }; //C语言风格

int[] a1[] 写在前面,更强调类型信息:数组int[]

int[] 也是继承了Object类, 而不是 Object[]

数组可以创建时赋值,或者创建后再赋值

1
2
3
4
5
6
7
8
int[] a2 = new int[] { 1, 2, 3 };
int a3[] = new int[] { 1, 2, 3 };
int[] a4 = new int[3];
for (int i = 0; i< a4.length; i++)
a4[i] = i;

数组长度是 length,而字符串String获取长度是length()

foreach 循环

JDK 5.0 引进了被称为foreach循环来遍历数组

1
2
for(int element: a4)
System.out.println(element);

输出

1
2
3
0
1
2

数组作为方法的参数

如果方法的参数是 int[]

  • 一是将变量传进去
  • 二是匿名格式 new int[] { 1, 2, 3 }, 传{ 1, 2, 3 }是错误的
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public static void main(String[] args) {
// testArr(a4); // 传入数组的引用,正确
testArr(new int[] { 1, 2, 3 });
// compile error
// testArr({ 1, 2, 3 });
// compile error
// testArr(1, 2, 3);
}
public static void testArr(int[] nums) {
for (int element : nums)
System.out.println(element);
}

输出

1
2
3
1
2
3

对于可变长参数的方法

1
2
3
4
public static void testVariPara(int... nums) {
for (int element : nums)
System.out.println(element);
}

下面这两种传递参数方法都可以正常运行

1
2
testVariPara(new int[] { 1, 2, 3 });
testVariPara(1, 2, 3);

Arrays 工具类

toString

Java 没有覆盖(override) 数组int[] 的toString()方法, 所以

1
System.out.println(a1.toString());

得到结果

1
[I@182da3d

可以使用工具类 Arrays 输出数组

1
System.out.println(Arrays.toString(a1));

输出

1
[1, 2, 3]

copyOf

1
2
3
4
5
6
7
int[] arr = {1,2,3,4,5};
int[] copied = Arrays.copyOf(arr, 10); //10 是数组的长度
System.out.println(Arrays.toString(copied));
copied = Arrays.copyOf(arr, 3);
System.out.println(Arrays.toString(copied));

输出

1
2
[1, 2, 3, 4, 5, 0, 0, 0, 0, 0]
[1, 2, 3]

区别 System.arraycopy()

1
2
3
4
5
6
int[] arr = {1,2,3,4,5};
int[] copied = new int[10];
System.arraycopy(arr, 0, copied, 1, 5);// 5是需复制的元素个数
System.out.println(Arrays.toString(copied));

输出

1
2
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[0, 1, 2, 3, 4, 5, 0, 0, 0, 0]

查看 Arrays.copyOf 源码可知,它是新建了一个数组,然后利用System.arraycopy 将源数组复制到新数组中

1
2
3
4
5
public static int[] copyOf(int[] original, int newLength) {
int[] copy = new int[newLength];
System.arraycopy(original, 0, copy, 0, Math.min(original.length, newLength));
return copy;
}

asList 使用

1
2
int[] a1 = { 1, 2, 3 };
List<int[]> list = Arrays.asList(a1); // eclipse 提示修改为这样可以编译通过

上面的很奇怪, List<int>List<Integer> 会编译不通过,只能改为 List<int[]>

转为 List<int[]> 可以通过这样访问数组 list.get(0)

为什么会这样呢?

List<int>编译错误,说明没有自动装箱和拆箱,Arrays.asList常见用法如下

1
List<Object> objList = Arrays.asList(obj1, obj2, obj3);

上面代码将多个输入参数转化为 List,你也可以传入单个参数

1
List<Object> objList = Arrays.asList(obj1);

而前面也提到了 int[]是一个对象,继承自Object , 所以返回了类型参数为 int[]List

推荐 Apache Commons ArrayUtils
下载地址 https://commons.apache.org/proper/commons-lang/download_lang.cgi

它提供了很多工具方法,可以使用下面的代码处理 int 数组

1
List<Integer> exampleList = Arrays.asList(ArrayUtils.toObject(array));

看下 API,public static Integer[] toObject(int[] array) 是将 int[] 转为 Integer[]

也可以这么使用 Arrays.asList 来转化数组

1
2
3
Integer[] a1 = { 1, 2, 3 };
List<Integer> list = Arrays.asList(a1);
System.out.println(list);

或者这样

1
2
List<Integer> list = Arrays.asList(1, 2, 3);
System.out.println(list);

asList 定长

1
2
List<Integer> list = Arrays.asList(1, 2, 3);
list.add(44); // 抛出 UnsupportedOperationException

上面的代码会抛出java.lang.UnsupportedOperationException异常

注意:Arrays.asList返回的是 Arrays.ArrayList,不是我们常用的ArrayList

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
// Arrays.ArrayList 源码
private static class ArrayList<E> extends AbstractList<E> {
public E get(int index) {
return a[index];
}
public E set(int index, E element) {
E oldValue = a[index];
a[index] = element;
return oldValue;
}
public int indexOf(Object o) {
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;
}
public boolean contains(Object o) {
return indexOf(o) != -1;
}
}

Arrays.asList 不支持改变数组长度的操作(addremove等),看下源码,Arrays.ArrayList只实现了 getsetindexOfcontains等方法。

Arrays.ArrayList的父类是AbstractList,它的 addset都是throw new UnsupportedOperationException()

1
2
3
4
5
6
7
8
9
10
// AbstractList 源码
public abstract class AbstractList<E> extends AbstractCollection<E> implements List<E> {
public void add(int index, E element) {
throw new UnsupportedOperationException();
}
public E remove(int index) {
throw new UnsupportedOperationException();
}
}

如果需要得到支持addremoveArrayList,可以这样

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

集合转为数组

ArrayList 可以转化为数组,有两个重载的方法

  • Object[] toArray()
  • <T> T[] toArray(T[] a)

注意 toArray() 只能得到 Object[],无法转型为具体数组类型,比如下面的代码会抛出java.lang.ClassCastException异常

1
2
List<Integer> list = new ArrayList<>(Arrays.asList(1, 2, 3));
Integer[] arr = (Integer[]) list.toArray(); // 抛出ClassCastException

如果需要得到具体类型,需要使用toArray(T[] a),传入参数T[] a

  • T[] a长度< 原来list的长度,则新建一个list的长度数组,填充元素并返回

  • 若长度 >= list的长度,则把list元素填充T[] a,并返回T[] a的引用

1
2
3
4
5
6
7
8
9
10
11
12
13
ArrayList<Integer> list = new ArrayList<>(Arrays.asList(1, 2, 3));
Integer[] a1 = new Integer[2];
list.toArray(a1);
System.out.println(Arrays.toString(a1));
Integer[] a2 = new Integer[3];
list.toArray(a2);
System.out.println(Arrays.toString(a2));
Integer[] a3 = new Integer[4];
list.toArray(a3);
System.out.println(Arrays.toString(a3));

输出

1
2
3
[null, null]
[1, 2, 3]
[1, 2, 3, null]

也可以这么使用String[] a = list.toArray(new String[0]);new String[0]只是为了类型信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// toArray(T[] a) 源码
public <T> T[] toArray(T[] a) {
if (a.length < size)
// Make a new array of a's runtime type, but my contents:
return (T[]) Arrays.copyOf(elementData, size, a.getClass());
System.arraycopy(elementData, 0, a, 0, size);
if (a.length > size)
a[size] = null;
return a;
}
public static <T,U> T[] copyOf(U[] original, int newLength, Class<? extends T[]> newType) {
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;
}

参考文章