Java集合深入学习 - ArrayList源码解析-3子List与坑点(基于jdk1.8)

本文深入探讨Java ArrayList的内部类SubList,解析其工作原理和可能遇到的坑点。1. SubList是一个内部类,用于展示ArrayList的部分数据,操作直接作用于原始ArrayList。2. 存在一个陷阱,Arrays.asList返回的ArrayList不支持add和remove操作,尝试这些操作会抛出异常。3. ArrayList的数据拷贝是浅拷贝,主要通过System.arraycopy和Arrays.copyOf实现,但都是表面级别的复制。
摘要由CSDN通过智能技术生成

Java集合深入学习 - ArrayList源码解析-1基础(基于jdk1.8)

Java集合深入学习 - ArrayList源码解析-2序列化与迭代器(基于jdk1.8)

Java集合深入学习 - ArrayList源码解析-3子List与坑点(基于jdk1.8)

1.内部类SubList解析

ArrayList中定义了一个内部类,用来暴露集合的部分数据,其定义代码如下:

   /**
     * 定义一个视图类,展示集合的部分数据 继承自AbstractList实现RandomAccess接口
     */
    private class SubList extends AbstractList<E> implements RandomAccess {
        private final AbstractList<E> parent;	//父类的引用
        private final int parentOffset;		//父数组偏移量
        private final int offset;		//偏移量
        int size;			//视图大小

        /**
         * 构造方法
         * @param parent
         * @param offset
         * @param fromIndex
         * @param toIndex
         */
        SubList(AbstractList<E> parent,
                int offset, int fromIndex, int toIndex) {
            this.parent = parent;	
            this.parentOffset = fromIndex;
            this.offset = offset + fromIndex;
            this.size = toIndex - fromIndex;
            this.modCount = ArrayList.this.modCount;
        }
}

其中定义了如下的方法

观察其源码可发现,针对SubList的操作(增删改查)都是直接操作在ArrayList上的,部分代码如下:

        /**
         * 修改数据
         */
        public E set(int index, E e) {
            rangeCheck(index);	//校验index位置
            checkForComodification();	//校验操作次数
            E oldValue = ArrayList.this.elementData(offset + index);	//获取父类中对应位置的数据	
            ArrayList.this.elementData[offset + index] = e;	//直接修改父数组中的数据  注:原数组数据会发生改变
            return oldValue;	//返回修改前的数据
        }
        
        /**
         * 获取数据
         */
        public E get(int index) {
            rangeCheck(index);
            checkForComodification();
            return ArrayList.this.elementData(offset + index);
        }
        
        /**
         * 获取大小
         */
        public int size() {
            checkForComodification();
            return this.size;
        }

        /**
         * 添加数据
         */
        public void add(int index, E e) {
            rangeCheckForAdd(index);	//校验index
            checkForComodification();
            parent.add(parentOffset + index, e); //调用父类添加数据方法添加数据
            this.modCount = parent.modCount;
            this.size++;
        }
        
        /**
         * 移除数据
         */
        public E remove(int index) {
            rangeCheck(index);
            checkForComodification();
            E result = parent.remove(parentOffset + index);
            this.modCount = parent.modCount;
            this.size--;
            return result;
        }

获取SubList的方式:

    /**
     * 获取当前集合部分数据的一个视图,数据为重fromIndex到toIndex
     */
    public List<E> subList(int fromIndex, int toIndex) {
        subListRangeCheck(fromIndex, toIndex, size);
        return new SubList(this, 0, fromIndex, toIndex);
    }
    /**
     * 大小关系校验   对应大小关系应为:0 < fromIndex < toIndex < size
     */
    static void subListRangeCheck(int fromIndex, int toIndex, int size) {
        if (fromIndex < 0)
            throw new IndexOutOfBoundsException("fromIndex = " + fromIndex);
        if (toIndex > size)
            throw new IndexOutOfBoundsException("toIndex = " + toIndex);
        if (fromIndex > toIndex)
            throw new IllegalArgumentException("fromIndex(" + fromIndex +
                                               ") > toIndex(" + toIndex + ")");
    }

2.坑点

2.1 另一个ArrayList之Arrays.asList

    /**
     * Arrays.asList根据动态传入的参数 生成一个ArrayList
     * @param a 动态参数 接收为 T[] 数组
     */
    @SafeVarargs
    @SuppressWarnings("varargs")
    public static <T> List<T> asList(T... a) {
        return new ArrayList<>(a);
    }

    /**
     * Arrays中的内部类ArrayList继承AbstractList实现RandomAccess和Serializable
     */
    private static class ArrayList<E> extends AbstractList<E>
        implements RandomAccess, java.io.Serializable{}

Arrays.ArrayList中实现的方法如下:

观察可发现,Arrays.ArrayList中并没有实现add/remove等方法,如果调用其add方法或remove方法,会调用到AbstractList中的方法,会直接抛出异常,其代码如下:

	/**
	 * AbstractList.add
	 */
    public boolean add(E e) {
        add(size(), e);
        return true;
    }
    
    /**
     * AbstractList.add
     */
    public void add(int index, E element) {
        throw new UnsupportedOperationException();
    }
   
    /**
     * AbstractList.remove
     */
    public E remove(int index) {
        throw new UnsupportedOperationException();
    }

2.2 数据处理都只是浅拷贝?

细读ArrayList源码,涉及到数据拷贝的地方有很多,其中大致调用了两个方法System.arraycopy和Arrays.copyOf,观察其源码会发现,其实Arrays.copyOf的处理也是调用了System.arraycopy方法,代码如下:

    /**
     * Arrays.copyOf
     */
    @SuppressWarnings("unchecked")
    public static <T> T[] copyOf(T[] original, int newLength) {
        return (T[]) copyOf(original, newLength, original.getClass());
    }
	/**
	 * Arrays.copyOf
	 */
    public static <T,U> T[] copyOf(U[] original, int newLength, Class<? extends T[]> newType) {
        @SuppressWarnings("unchecked")
        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;
    }

所以,需要特别注意的地方有如下:

		//该集合不能够进行增删操作
		List<Demo> list = Arrays.asList(new Demo("111"),new Demo("222"),new Demo("333"));
		
		List list1 = new ArrayList<>();
		list1.add("");//添加数据
		
		//这种方式创建的数组  源集合中数据发生改变时,list2中的数据也会发生改变
		List list2 = new ArrayList<>(list1); 
		
		
		List list3 = new ArrayList<>();
		//这两种方式创建的数组  源集合中数据发生改变时,list3中的数据也会发生改变
		list3.addAll(list2);
		list3.addAll(0, list2);
		
		//objs中数据发生改变,list3中数据也会发生改变
		Object[] objs = list3.toArray();

本文连接,转载请标注https://blog.csdn.net/luo_mu_hpu/article/details/106216169

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值