Arrays列表操作常见的坑点

  • 2020-03-31
  • 361
  • 1

坑1:不能直接使用Arrays.asList来转换基本类型数组

错误示例

 int[] arr = {1, 2, 3};
    List list = Arrays.asList(arr);
    System.out.println("list:{ " + list + "} size:{ " + list.size() + "} class:{ " + list.get(0).getClass() + "}");

现象

预期结果是list集合中会包含三个数组元素,每个元素应该是一个整数类型的数据,但是实际上只有一个,而且元素的类型是一个整数数组。

20:00:56.434 [main] INFO com.taobao.gpf.metis.CompConfigTest – list:[[I@735f7ae5] size:1 class:class [I

原因分析

只能是把 int 装箱为 Integer,不可能把 int 数组装箱为 Integer 数组。我们知道,Arrays.asList 方法传入的是一个泛型 T 类型可变参数,最终 int 数组整体作为了一个对象成为了泛型类型 T:

@SuppressWarnings("varargs")
public static <T> List<T> asList(T... a) {
    return new ArrayList<>(a);
}

解决方案

  • 解决方案是把int转换成Integer类型
  • 使用Arrays.stream装箱

正确实例

int[] arr1 = {1, 2, 3};
List list1 = Arrays.stream(arr1).boxed().collect(Collectors.toList());
log.info("list:{} size:{} class:{}", list1, list1.size(), list1.get(0).getClass());


Integer[] arr2 = {1, 2, 3};
List list2 = Arrays.asList(arr2);
log.info("list:{} size:{} class:{}", list2, list2.size(), list2.get(0).getClass());

20:01:49.075 [main] INFO com.taobao.gpf.metis.CompConfigTest – list:[1, 2, 3] size:3 class:class java.lang.Integer
20:01:49.081 [main] INFO com.taobao.gpf.metis.CompConfigTest – list:[1, 2, 3] size:3 class:class java.lang.Integer

坑2:Arrays.asList返回的List不支持增删操作

错误示例

String[] arr = {"1", "2", "3"};
List list = Arrays.asList(arr);
arr[1] = "4";
try {
    list.add("5");
} catch (Exception ex) {
    ex.printStackTrace();
}
log.info("arr:{} list:{}", Arrays.toString(arr), list);

结果

java.lang.UnsupportedOperationException
at java.util.AbstractList.add(AbstractList.java:148)
at java.util.AbstractList.add(AbstractList.java:108)
at com.taobao.gpf.metis.CompConfigTest.main(CompConfigTest.java:34)

原因分析

从上图可以看到抛出了UnsupportedOperationException异常,这个是因为Arrays.asList返回的List并不是我们期望的java.util.ArrayList,而是Arrays的内部类ArrayList。ArrayList内部类继承自AbstractList类,并没有覆写父类的add方法,所以在使用add方法时,直接调用到了父类的add方法,父类的add方法中会直接抛出UnsupportedOperationException。

/**
* @serial include
*/
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);
}
}
/**
* {@inheritDoc}
*
* <p>This implementation always throws an
* {@code UnsupportedOperationException}.
*
* @throws UnsupportedOperationException {@inheritDoc}
* @throws ClassCastException {@inheritDoc}
* @throws NullPointerException {@inheritDoc}
* @throws IllegalArgumentException {@inheritDoc}
* @throws IndexOutOfBoundsException {@inheritDoc}
*/
public void add(int index, E element) {
throw new UnsupportedOperationException();
}

坑3:对原始数组的修改会影响到我们获得的那个List

原因分析

看一下 ArrayList 的实现,可以发现 ArrayList 其实是直接使用了原始的数组。所以,我们要特别小心,把通过 Arrays.asList 获得的 List 交给其他方法处理,很容易因为共享了数组,相互修改产生 Bug。

解决办法

重新New一个ArrayList对象即可,这样原始数组和新的结合是两块不同的内存区域,互不影响。

 String[] arr = {"1", "2", "3"};
 List list = new ArrayList(Arrays.asList(arr));
 arr[1] = "4";
 try {
       list.add("5");
  } catch (Exception ex) {
       ex.printStackTrace();
  }
 log.info("arr:{} list:{}", Arrays.toString(arr), list);

20:03:42.091 [main] INFO com.taobao.gpf.metis.CompConfigTest – arr:[1, 4, 3] list:[1, 2, 3, 5]

感谢打赏!
微信

评论

  • 今日新鲜事回复

    文章不错非常喜欢