【工作笔记】Arrays.asList

起因

我看到工作群里有人说扫描代码时候,提示了代码的用法错误。我看了一眼截图,代码块里有类似 List list = Arrays.asList() 和 list.add() 的使用。第一反应就是阿里开发规范里,禁止在这里使用 add() 方法

【强制】使用工具类 Arrays.asList()把数组转换成集合时,不能使用其修改集合相关的方法,它的 add/remove/clear 方法会抛出 unsupportedOperationException 异常
说明:asList 的返回对象是一个 Arrays 内部类,并没有实现集合的修改方法。Arrays.asList 体现的是适配器模式,只是转换接口,后台的数据仍是数组
    String[] str = new String[] { "chen", "yang", "hao" };
    List list = Arrays.asList(str);
第一种情况:list.add(“yangguanbao”); 运行时异常
第二种情况:str[0] = “change”; 也会随之修改,反之亦然
探究

其实我也只是知道不能这么用,具体没仔细看过为什么。抱着一探究竟的目的我决定看看源码怎么写的

  • 示例代码
    public class AsListTest {

        private static final Logger logger = LoggerFactory.getLogger(AsListTest.class);

        public static void main(String[] args) {
            String[] str = new String[]{"zhao", "qian", "sun"};
            List list = Arrays.asList(str);
            logger.info("list.toString:{}, list:{}", list.toString(), list);
            str[2] = "li";
            logger.info("list.toString:{}, list:{}", list.toString(), list);
            list.set(1, "li");
            logger.info("list.toString:{}, list:{}", list.toString(), list);
            list.remove(1);
            list.add("li");

            logger.info("list.toString:{}, list:{}", list.toString(), list);

        }

    }

    // 输出
    // list:[zhao, qian, sun]
    // list:[zhao, qian, li]
    // list:[zhao, li, li]
    // Exception in thread "main" java.lang.UnsupportedOperationException
	//     at java.util.AbstractList.remove(AbstractList.java:161)
	//     at com.just.hungry.test.AsListTest.main(AsListTest.java:24)

  • 源码
    /**
     * Returns a fixed-size list backed by the specified array.  (Changes to
     * the returned list "write through" to the array.)  This method acts
     * as bridge between array-based and collection-based APIs, in
     * combination with {@link Collection#toArray}.  The returned list is
     * serializable and implements {@link RandomAccess}.
     *
     * <p>This method also provides a convenient way to create a fixed-size
     * list initialized to contain several elements:
     * <pre>
     *     List&lt;String&gt; stooges = Arrays.asList("Larry", "Moe", "Curly");
     * </pre>
     *
     * @param <T> the class of the objects in the array
     * @param a the array by which the list will be backed
     * @return a list view of the specified array
     */
    @SafeVarargs
    @SuppressWarnings("varargs")
    public static <T> List<T> asList(T... a) {
        // 调用了私有的静态方法,生成新的 ArrayList
        return new ArrayList<>(a);
    }

    /**
     * @serial include
     */
    // 继承了 AbstractList
    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(str) 的作用正如接口上注释所描述:(1)返回由指定数组支持的固定大小的集合;(2)提供了一种方便的方法来创建固定大小的集合
  2. 这里有个经典的设计模式,适配器模式。ArrayList 这个内部抽象方法继承了 AbstractList 的部分方法,而 AbstractList 实现了 List 的方法。AbstractList 里边的实现方法大多都是如下所示的那样的,而真正的实现逻辑都是继承了 AbstractList 的子类来实现。这样你想要实现什么方法直接继承 AbstractList,然后去实现就好了,如果有些 AbstractList 的方法你不需要就不用实现了。关于适配器模式可以参见【设计模式】适配器模式(Adapter)
        public void add(int index, E element) { 
            throw new UnsupportedOperationException();
        } 
    
  3. ArrayList 实现的方法,例如 get(int index),set(int index, E element) 等。这也是为什么测试案例的 list.set(1, “li”); 可以正常输出的原因
  4. ArrayList 没实现的方法,例如 add(int index, E element),remove(int index) 等。所以你在使用这些方法的时候直接引用了父类的实现,而父类的实现就如上所示-直接抛出异常。这也是为什么异常信息提示 UnsupportedOperationException 的原因
  5. 至于 ArrayList 为什么没有实现这些方法,从源码上看,ArrayList 维护了一个 final 修饰的数组 a,这样就设定了长度的不可变。从功能上看,这个内部类的目的如第一点所示,只是创建固定大小的集合而不是对集合进行修改
  6. 如果想进行创建和修改,可以使用 new Arraylist<Arrays.asList(str)>
补充:其他数组转集合的方式
public class TestArrays2List {

    @Test
    public void testArrayCastToListError() {
        String[] strArray = new String[]{"1","2","1"};
        List<String> list = Arrays.asList(strArray);
        list.add("1");
        System.out.println(list);
    }

    @Test
    public void testArrayCastToListRight() {
        String[] strArray = new String[]{"1","2","1"};
        ArrayList<String> list = new ArrayList<>(Arrays.asList(strArray)) ;
        list.add("1");
        System.out.println(list);
    }

	// 数组转集合时效率最高
	// 单个元素添加是效率和ArrayList.addAll 差不多
	// 集合添加集合时效率不如 ArrayList.addAll 
    @Test
    public void testArrayCastToListEfficient() {
        String[] strArray = new String[]{"1","2","1"};
        ArrayList< String> arrayList = new ArrayList<>(strArray.length);
        Collections.addAll(arrayList, strArray);
        arrayList.add("1");
        System.out.println(arrayList);
    }

    @Test
    public void testArrayCastToStream1() {
        String[] strArray = new String[]{"1","2","1"};
        List<String> stringList= Stream.of(strArray).collect(Collectors.toList());
        stringList.add("1");
        System.out.println(stringList);
    }

    @Test
    public void testArrayCastToStream2() {
        long[] strArray = new long[]{1L,2L,3L};
        List<Long> stringList= Arrays.stream(strArray).boxed().collect(Collectors.toList());
        stringList.add(1L);
        System.out.println(stringList);
    }

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值