头疼,利用 Arrays.asList() 方法生成的 ArrayList 列表不能添加或者删除元素,还给我抛异常 java.lang.UnsupportedOperationException

在日常开发中,相信大家或多或少都有用到过一个类 Arrays ,这个类具备的功能还是比较实用的,比如用它做数组的排序,直接 Arrays.sort(数组名) 就搞定,又比如用它将一个数组转换成一个 ArrayList 列表,使用 Arrays.asList(数组名) 就能完成,用起来,哎,挺香的!

美好的事物大家都喜欢,但意外总是存在的,指不定哪天就到来破坏了这份美好,这不,最近我在用 Arrays.asList() 这个方法处理数组时就翻了车,就差找个墙角裂缝钻进去,躲起来了。

有点扯远了,回归正题,结论先行,这里先说结论:利用 Arrays.asList() 方法生成的 ArrayList 列表不能添加或者删除元素

我反手就是一问,为什么呀?别急,下面一起来剖析一下原因。

🔴问题复现

直接用 IDEA 建一个测试类 TestArrayList.java ,然后写上 main 方法,内容如下:

package com.javabasic.practice;

import java.util.Arrays;
import java.util.List;

/**
 * @Description 测试通过Arrays.asList()生成的ArrayList列表不能添加、移除列表元素的原因
 * @ClassName TestArrayList
 * @Author yuhuofei
 * @Date 2023/3/26 16:02
 * @Version 1.0
 */
public class TestArrayList {
    public static void main(String[] args) {
        String[] strings = {"苹果", "桃子", "橙子", "青枣"};
        List<String> list = Arrays.asList(strings);
        System.out.println("当前的list列表是:" + list);
    }
}

运行结果如下图所示,可以看到,这时是能正常完成一个由数组转换成列表的过程的。

在这里插入图片描述

添加元素

我们尝试往上面生成的列表里面添加新的元素,比如添加一个“火龙果”,代码如下:

package com.javabasic.practice;

import java.util.Arrays;
import java.util.List;

/**
 * @Description 测试通过Arrays.asList()生成的ArrayList列表不能添加、移除列表元素的原因
 * @ClassName TestArrayList
 * @Author yuhuofei
 * @Date 2023/3/26 16:02
 * @Version 1.0
 */
public class TestArrayList {
    public static void main(String[] args) {
        String[] strings = {"苹果", "桃子", "橙子", "青枣"};
        List<String> list = Arrays.asList(strings);
        System.out.println("当前的list列表是:" + list);
        //尝试往list列表中添加一个元素"火龙果"
        list.add("火龙果");
        System.out.println("添加元素后的list列表是:" + list);
    }
}

好家伙,这次直接报错了,莫名其妙的,不信你看看图片里的内容。

在这里插入图片描述

删除元素

既然添加元素报错,那我们就再试试删除元素又是什么情况,代码稍微改一下:

package com.javabasic.practice;

import java.util.Arrays;
import java.util.List;

/**
 * @Description 测试通过Arrays.asList()生成的ArrayList列表不能添加、移除列表元素的原因
 * @ClassName TestArrayList
 * @Author yuhuofei
 * @Date 2023/3/26 16:02
 * @Version 1.0
 */
public class TestArrayList {
    public static void main(String[] args) {
        String[] strings = {"苹果", "桃子", "橙子", "青枣"};
        List<String> list = Arrays.asList(strings);
        System.out.println("当前的list列表是:" + list);
        //尝试删除list列表中的第一个元素
        list.remove(0);
        System.out.println("删除元素后的list列表是:" + list);
    }
}

同样地,我们运行起来,跑一下看看结果,发现一样地,它也报错了。

在这里插入图片描述

🔴原因剖析

这个时候,你是不是脑海里一堆为什么为什么在环绕,甚至还有点意外?要搞清楚上面的问题,那我们得关注 Arrays 这个类的源码实现了。

在上面的代码中,我们是通过 List< String > list = Arrays.asList(strings); 这行代码生成列表的,按住 Ctrl 键,点击鼠标左键,通过 asList 方法进到 Arrays.java 这个类。

在这里插入图片描述
为了方便查看,这里把上图相关的源码内容复制出来,其实主要就两个东西,一个是 asList 方法,另一个就是 ArrayList 这个静态内部类,它归属于 Arrays 类。

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

/**
 * @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);
    }
}

我们来分析一下上面的源码:

  1. 当外部调用 Arrays.asList() 方法时,其实就是调用了下面的这个方法,而下面这个方法的实现又调用 new ArrayList<>(a) 这个构造方法

    public static <T> List<T> asList(T... a) {
        return new ArrayList<>(a);
    }
    
  2. new ArrayList<>(a) 这个构造方法属于谁?它是属于 private static class ArrayList 这个静态内部类的,由下面的这行代码可知,这个静态内部类,继承了 AbstractList 这个抽象类,并且还实现了两个接口

    private static class ArrayList<E> extends AbstractList<E> implements RandomAccess, java.io.Serializable
    
  3. 具体来看下面的这两行代码,第一行里面有一个 final 关键字,这说明这个数组的长度一旦确定就是不可变的,而第二行则是 private static class ArrayList 这个静态内部类的有参构造方法,也是通过 Arrays.asList() 方法创建 ArrayList 列表的关键点所在,有它存在才有列表产生

        private final E[] a;
    
        ArrayList(E[] array) {
            a = Objects.requireNonNull(array);
        }
    
  4. 仔细查看 private static class ArrayList 这个静态内部类,我们会发现,其实它里面并没有 add 和 remove 方法

到这里,我们大致能明白,前面通过 List< String > list = Arrays.asList(strings); 这行代码生成的 ArrayList 列表对象其实是用 Arrays 这个类里面的 private static class ArrayList 这个静态内部类创建的,这个静态内部类它本身并没有 add 和 remove 方法。

那么问题来了,为什么前面的代码能调用 add 和 remove 方法?那是因为继承了一个抽象类 AbstractList ,这个抽象类里面有 add 和 remove 方法,不过只是声明,没有具体的实现逻辑,内容如下:

在这里插入图片描述

public void add(int index, E element) {
     throw new UnsupportedOperationException();
 }

 /**
  * {@inheritDoc}
  *
  * <p>This implementation always throws an
  * {@code UnsupportedOperationException}.
  *
  * @throws UnsupportedOperationException {@inheritDoc}
  * @throws IndexOutOfBoundsException     {@inheritDoc}
  */
 public E remove(int index) {
     throw new UnsupportedOperationException();
 }

由代码可知,前面的报错 UnsupportedOperationException ,也是由这里抛出的,至此这一切就都明朗了。而我们用 public class ArrayList 这个类 new 出来的对象,能进行列表元素的添加和删除,其实就在于这个类它自己实现了 add 和 remove 方法,不信你去看源码,肯定是这样。

🔴小结

利用 Arrays.asList() 方法生成的 ArrayList 列表不能添加或者删除元素的原因:

  1. 有 final 关键字修饰底层数组,数组长度一旦确定不可变
  2. 最关键也是最根本的原因是,Arrays 里面的 ArrayList 这个静态内部类,本身没有实现 add 和 remove 方法

如果还是想要实现添加或者删除元素,那该怎么办?别慌,改一行代码就可以搞定了。

List<String> list = new ArrayList<>(Arrays.asList(strings));

在这里插入图片描述

  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值