J.Nemo

Stay Hungry, Stay Foolish

Arrays.asList的坑

简介

工具类Arrays.asList()方法在开发中常见的坑,重点集中转化之后调用其add/remove/clear方法的时候会出现抛出UnsupportedOperationException()的情况。

分析

测试代码中,只有转化String[] myArray3 = {"a","b","c"};时,没有飘红,但是依旧会出现UnsupportedOperationException()的情况。

1
2
3
4
5
6
7
8
9
10
public class TestAsList {
public static void main(String[] args) {
Integer[] myArray = {1, 2, 3};
int[] myArray2 = {4, 5, 6};
String[] myArray3 = {"a", "b", "c"};
List<String> list1 = Arrays.asList(myArray3);
list1.add("d");
System.out.println(list1);
}
}

查看源码

1
2
3
4
5
6
7
8
9
10
public class TestAsList {
public static void main(String[] args) {
Integer[] myArray = {1, 2, 3};
int[] myArray2 = {4, 5, 6};
String[] myArray3 = {"a", "b", "c"};
List<String> list1 = Arrays.asList(myArray3);
//list1.add("d");
System.out.println(list1.getClass());
}
}

得到当前的类是java.util.Arrays$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
30
31
32
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;
}

发现Arrays看上去是个很正常的方法,然而实际上你点进到ArrayList发现,其实ArrayList并不是我们平时用的ArrayList。而是Arrays里面的一个内部类。而且这个内部类没有add/clear/remove方法,所以抛出的异常其实来自于AbstractList。

1
2
3
4
5
6
7
 public void add(int index, E element) {
throw new UnsupportedOperationException();
}

public E remove(int index) {
throw new UnsupportedOperationException();
}

抛出异常的地方,clear底层也会调用到remove所以也会抛出异常。

解决办法

1.通过一个循环遍历

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// for Java 1.5+
static <T> List<T> arrayToList(final T[] array) {
final List<T> l = new ArrayList<T>(array.length);

for (final T s : array) {
l.add(s);
}
return (l);
}

// for Java < 1.5 (no generics, no compile-time type-safety, boo!)
static List arrayToList(final Object[] array) {
final List l = new ArrayList(array.length);

for (int i = 0; i < array.length; i++) {
l.add(array[i]);
}
return (l);
}

2.最简便的方法(推荐)

1
List list = new ArrayList<>(Arrays.asList("a", "b", "c"))

3.使用 Java8 的Stream(推荐)

1
2
3
4
5
Integer [] myArray = { 1, 2, 3 };
List myList = Arrays.stream(myArray).collect(Collectors.toList());
//基本类型也可以实现转换(依赖boxed的装箱操作)
int [] myArray2 = { 1, 2, 3 };
List myList = Arrays.stream(myArray2).boxed().collect(Collectors.toList());

4. 使用 Guava(推荐)

对于不可变集合,你可以使用ImmutableList类及其of()copyOf()工厂方法:(参数不能为空)

1
2
List<String> il = ImmutableList.of("string", "elements");  // from varargs
List<String> il = ImmutableList.copyOf(aStringArray); // from array

对于可变集合,你可以使用Lists类及其newArrayList()工厂方法:

1
2
3
List<String> l1 = Lists.newArrayList(anotherListOrCollection);    // from collection
List<String> l2 = Lists.newArrayList(aStringArray); // from array
List<String> l3 = Lists.newArrayList("or", "string", "elements"); // from varargs

5. 使用 Apache Commons Collections

1
2
List<String> list = new ArrayList<String>();
CollectionUtils.addAll(list, str);

解决的办法采取stackoverflow以及JavaGuide的文章