zoukankan      html  css  js  c++  java
  • java 容器(collection)--ArrayList 常用方法分析 源码分析

    ArrayList 介绍

    打开jdk源码看看官方文档的介绍
    

    在这里插入图片描述
    粗糙的翻译下大致意思是:
    List接口的可调整大小的数组实现。实现了所有可选的列表操作,并允许所有元素,包括 null 。除了实现List接口之外,这个类提供了操作数组大小的方法。

    ArrayList定义的属性

     /**
         * 默认容量大小10
         */
        private static final int DEFAULT_CAPACITY = 10;
    
        /**
         * 空构造器调用
         */
        private static final Object[] EMPTY_ELEMENTDATA = {};
        private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
    
        /**
         * 缓冲区
         */
        transient Object[] elementData; // non-private to simplify nested class access
    
        /**
         * 元素个数
         */
        private int size;
    
    

    无参构造器

      /**
         * Constructs an empty list with an initial capacity of ten.
         */
        public ArrayList() {
            this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
        }
    

    这个相当于创建一个空数组:
    this.elementData={};

    有参构造器

    /**
         * Constructs an empty list with the specified initial capacity.
         *
         * @param  initialCapacity  the initial capacity of the list
         * @throws IllegalArgumentException if the specified initial capacity
         *         is negative
         */
        public ArrayList(int initialCapacity) {
            if (initialCapacity > 0) {
                this.elementData = new Object[initialCapacity];
            } else if (initialCapacity == 0) {
                this.elementData = EMPTY_ELEMENTDATA;
            } else {
                throw new IllegalArgumentException("Illegal Capacity: "+
                                                   initialCapacity);
            }
        }
    

    他的健壮性比较好,我们按正常思维,只看第一个 initialCapacity > 0 的 if 分支:
    相当于创建一个长度为 20 的数值:
    this.elementData=new Object[20];

    添加方法 add(Object obj)

    public boolean add(E e) {
            ensureCapacityInternal(size + 1);  // Increments modCount!!
            elementData[size++] = e;
            return true;
        }
    
    

    这个代码技术含量挺高,不愧是老司机写的
    ensureCapacityInternal(size + 1); 用来 检测空间容量是否够用(看下一个方法)
    elementData[size++] = e; 就是添加元素相当于:
    elementData[size]=e; size++;

    检测空间容量是否够用

     private void ensureCapacityInternal(int minCapacity) {
            ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
        }
    

    这个方法首先会在内部调用calculateCapacity(elementData, minCapacity) 计算容量:

    private static int calculateCapacity(Object[] elementData, int minCapacity) {
            if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
                return Math.max(DEFAULT_CAPACITY, minCapacity);
            }
            return minCapacity;
        }
    

    当我们在新建一个ArrayList 对象上添加元素时:
    Math.max(DEFAULT_CAPACITY, minCapacity); 相当于:
    Math.max(10,1);

    到这里容量计算完了,然后回去执行:

     private void ensureExplicitCapacity(int minCapacity) {
            modCount++;
    
            // overflow-conscious code
            if (minCapacity - elementData.length > 0)
                grow(minCapacity);
        }
    

    由于是第一次添加,if 条件相当于:
    if( 10 - 0 > 0)
    结果为true,会执行if语句块
    grow(minCapacity); 执行的就是扩容方法。

    扩容方法

     private void grow(int minCapacity) {
            // overflow-conscious code
            int oldCapacity = elementData.length;
            int newCapacity = oldCapacity + (oldCapacity >> 1);
            if (newCapacity - minCapacity < 0)
                newCapacity = minCapacity;
            if (newCapacity - MAX_ARRAY_SIZE > 0)
                newCapacity = hugeCapacity(minCapacity);
            // minCapacity is usually close to size, so this is a win:
            elementData = Arrays.copyOf(elementData, newCapacity);
        }
    

    我们是第一次添加,以上代码实际执行的就是:
    int oldCapactiy=0;
    int newCapacity=0+0>>1 结果为0
    如果 0-10<0 为 true
    newCapacity=10;
    数组拷贝 elementData的长度就是 10

    最后回到:
    public boolean add(E e) {
    ensureCapacityInternal(size + 1); // Increments modCount!!
    elementData[size++] = e;
    return true;
    }
    结果就是:
    elementData[0] = e;
    size++;

    接下来看下add 的重载方法

    add(int index, E element)

    public void add(int index, E element) {
            rangeCheckForAdd(index);
    
            ensureCapacityInternal(size + 1);  // Increments modCount!!
            System.arraycopy(elementData, index, elementData, index + 1,
                             size - index);
            elementData[index] = element;
            size++;
        }
    

    rangeCheckForAdd(index);是下标越界检测,我们先不看,下一行代码:
    ensureCapacityInternal(size + 1);跟之前的默认位置天机一样先检测数组的空间容量。
    关键是再往下一行代码;
    执行的结果是把要插入位置开始到结束的所有元素往后挪一个位置,然后把空出的位置赋值为传入有的参数,同时把元素个数+1;

    现在ArrayList对象的容量是10了,当我么一直做添加操作,容量满了会怎么样呢?
    其实不管我们怎么添加,都会执行ensureCapacityInternal(size + 1) 来检测容量,而ensureCapacityInternal(size + 1)又会调用calculateCapacity(elementData, minCapacity)直到
    ensureExplicitCapacity(int minCapacity)方法中的if语句成立时,执行
    grow(minCapacity);进行扩容:
    比如我们加到第11个元素时:
    grow(minCapacity)方法中的
    oldCapacity + (oldCapacity >> 1);返回15;
    那么ArrayList的容量就增加到15了;
    继续增加还是一样的操作。
    至此,我们已经对ArrayList 创建,添加,扩容有了一定了解。
    那么剩下的一些方法就是数组的操作了,很好理解。

    ArrayList的其他方法

    get(int index)根据索引获取元素对象

    public E get(int index) {
            rangeCheck(index);
    		//调用了elementData()的方法
            return elementData(index);
        }
     @SuppressWarnings("unchecked")
        E elementData(int index) {
        	//根据索引取出数组中索引对象
            return (E) elementData[index];
        }
    
    

    size()

      public int size() {
      		// 返回集合记录的元素个数
            return size;
        }
    

    isEmpty()

    public boolean isEmpty() {
    	// 这个太简单了,不必多说
            return size == 0;
        }
    

    set(int index, Object obj)

    public E set(int index, E element) {
            rangeCheck(index);
    
            E oldValue = elementData(index);
            elementData[index] = element;
            return oldValue;
        }
    

    就是数组操作,返回替换掉的元素

    remove(int index)

     public E remove(int index) {
            rangeCheck(index);
    
            modCount++;
            //根据索引获取原始元素对象
            E oldValue = elementData(index);
            //拷贝的元素的个数
            int numMoved = size - index - 1;
            if (numMoved > 0)
                System.arraycopy(elementData, index+1, elementData, index,
                                 numMoved);
            elementData[--size] = null; //   将最后一个位置设置为null
    
            return oldValue;
        }
    
    

    类似于前面的指定位置添加,返回原始元素对象

    clear()

    public void clear() {
            modCount++;
    
            // clear to let GC do its work
            for (int i = 0; i < size; i++)
                elementData[i] = null;
    
            size = 0;
        }
    

    循环遍历,所有 索引位置的元素赋值null,并把元素个数设置为0;

    水平有限,先写到这了。

    重视基础,才能走的更远。
  • 相关阅读:
    好久没锻炼
    liunx下安装第三方Python(PIP安装)
    Mysql和SqlServer互相转换
    sqlmap使用笔记
    查找域控的几个常用方法
    ssh的一些小操作
    使用theHarvester 进行邮箱和子域名的收集
    python实现大文件分割与合并
    python中MySQLdb模块用法实例
    2. Shell编程第二讲
  • 原文地址:https://www.cnblogs.com/xzlf/p/12681545.html
Copyright © 2011-2022 走看看