ArrayList源码及方法解析

基本参数:

  • static final int DEFAULT_CAPACITY = 10;

    • 默认初始容量为10
  • static final Object[] EMPTY_ELEMENTDATA = {};

    • 可以存放任何类型元素的空数组
  • transient Object[] elementData;

    • 对象数组,ArrayList的底层数据结构为数组
  • private int size;

    • elementData中已经存放的元素的个数,注意:不是elementData的容量

ArrayList的扩容:

  • private Object[] grow() {
            return grow(size + 1);   //传进数组元素个数+1的数量
        }
    
  •  private Object[] grow(int minCapacity) {
            int oldCapacity = elementData.length;    
            //假如数组长度不为空,使用ArraySupport.newLength方法扩容为原来的1.5倍
            if (oldCapacity > 0 || elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
                int newCapacity = ArraysSupport.newLength(oldCapacity,
                        minCapacity - oldCapacity, /* minimum growth */
                        oldCapacity >> 1           /* preferred growth */);
                        //数组长度右移1位,也就是除以2
                return elementData = Arrays.copyOf(elementData, newCapacity);
                	//将元素复制到原数组长度1.5倍的新数组
            } else {
                return elementData = new Object[Math.max(DEFAULT_CAPACITY, minCapacity)];
                //假如原数组长度为0,则初始化新的长度为10的数组
            }
        }
        
        
     =====================================================================================   //ArraySupport中的newLength方法:
        public static int newLength(int oldLength, int minGrowth, int prefGrowth) {
            // assert oldLength >= 0
            // assert minGrowth > 0
    
    		//oldLength为原数组长度,minGrowth为size+1减去原数组长度,preGrowth为原数组长度除以2
    
    
            int newLength = Math.max(minGrowth, prefGrowth) + oldLength;
            //比较minGrowth与preGrowth的大小,这里得到preGrowth较大,用preGrowth加上原数组长度,也就是用原数组长度×1.5
            if (newLength - MAX_ARRAY_LENGTH <= 0) {
                return newLength;
            }
            return hugeLength(oldLength, minGrowth);
        }
        
        
        
    

构造方法:

  • 有参构造:构造具有指定初始容量(即参数)的空列表

    • 如果可以预先知道数组大小,则使用有参构造指定数组大小,可以避免数组扩容提升性能
    • 如果初始容量为0时,数组为空,在进行添加元素时才会进行扩容创建需要的数组
    public ArrayList(int initialCapacity) {         //initialCapacity为自定义初始容量
            if (initialCapacity > 0) {              
                this.elementData = new Object[initialCapacity];     //初始值大于0,则创建该容量大小的数组elementData
            } else if (initialCapacity == 0) {
                this.elementData = EMPTY_ELEMENTDATA;     //初始值等于0,创建空的数组elementData
            } else {
                throw new IllegalArgumentException("Illegal Capacity: "+
                                                   initialCapacity);
            }     //初始值小于0,报错
        }
    
  • 空参构造:构造一个初始容量为10的空列表

     public ArrayList() {
            this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
        }
    

    ArrayList考虑到节省内存,仅仅是创建了ArrayList对象,初始化了一个空数组,在首次添加元素时,才真正初始化容量为10的数组

    在空参构造中,使用的是DEFAULTCAPACITY_EMPTY_ELEMENTDATA;空数组,而不是EMPTY_ELEMENTDATA,因为DEFAULTCAPACITY_EMPTY_ELEMENTDATA首次扩容为10,而EMPTY_ELEMENTDATA是按照1.5倍扩容从0开始而不是10

其他方法:

  • 查询指定元素出现的的索引:

       int indexOfRange(Object o, int start, int end) {
            Object[] es = elementData;         //将对象数组赋值给es数组
            if (o == null) {				   
                for (int i = start; i < end; i++) {
                    if (es[i] == null) {
                        return i;
                    }
                }							//如果传进的元素为null,经过遍历数组返回Null的索引
            } else {
                for (int i = start; i < end; i++) {
                    if (o.equals(es[i])) {
                        return i;
                    }
                }                        //如果传进的元素不为null,遍历数组找到该元素返回索引
            }
            return -1;
        }
        
        
      ============================================================================================  
        
        
        public int indexOf(Object o) {
            return indexOfRange(o, 0, size);
        }     									//计算索引的方法调用indexOfRange()方法,传进要查询的元素,并且从0开始,最多到数组中包含的元素个数
        
        
        
    
  • add()方法:

     public boolean add(E e) {
            modCount++;     //增加修改次数
            add(e, elementData, size);    //添加元素
            return true;
        }
        
        
        
     private void add(E e, Object[] elementData, int s) {
            if (s == elementData.length)   
                elementData = grow();			//如果容量不够进行扩容
            elementData[s] = e;     //数组扩容后,将元素个数作为下标,s-1是最后一个元素的索引,那么s就是要添加的元素所在的索引位置,所以将该位置赋值为e
            size = s + 1;   		//元素个数+1
        }   
        
        
    
    public void add(int index, E element) {
            rangeCheckForAdd(index);      	//检查Index是否越界
            modCount++;						//增加修改次数
            final int s;
            Object[] elementData;
            if ((s = size) == (elementData = this.elementData).length)
                elementData = grow();				//如果数组大小不够进行扩容
            System.arraycopy(elementData, index,
                             elementData, index + 1,
                             s - index);
            //从数组中的index位置开始,将index及index之后的元素复制进同一个数组中的index+1的位置,也就是从index开始,所有之后的元素都往后挪了一位,空出了index位置
            elementData[index] = element;			//将index赋值为新元素
            size = s + 1;
        }
    
     public static native void arraycopy(Object src,  int  srcPos,
                                            Object dest, int destPos,
                                            int length);
    		//将指定源数组中的数组从指定位置复制到目标数组的指定位置。 
    

    src :源数组、  srcPos:源数组中的起始位置、  dest:目标数组、 destPos:目标数组的起始位置、 length:要复制的数组元素的数目

  • addAll()方法

    public boolean addAll(Collection<? extends E> c) {
            Object[] a = c.toArray();     	//将集合转变为数组
            modCount++;					
            int numNew = a.length;			
            if (numNew == 0)
                return false;
            Object[] elementData;
            final int s;
            if (numNew > (elementData = this.elementData).length - (s = size))
                elementData = grow(s + numNew);    
                //如果将要添加的新集合的元素数量大于原集合的空位置,则将原数组进行扩容。扩容的大小必须至少能装下新集合
            System.arraycopy(a, 0, elementData, s, numNew);
            size = s + numNew;
            return true;
        }
    
    • add()方法中,每次扩容时,调用grow()空参方法,每次扩容1.5倍
    • 而addAll()方法每次扩容,调用的是grow(size+numNew)有参方法,假如要求扩容后的空间小于原数组的1.5倍时,还扩容1.5倍,如果大于,那就扩容到刚好装下新集合
    • addAll方法就是add方法的批量版本,优势在于避免了一个一个元素添加时的多次库容,提高了效率
  • contains方法:

    public boolean contains(Object o) {
            return indexOf(o) >= 0;
        }
    //计算要查询元素的索引,当索引值>=0时,返回true
    
  • 集合转数组:toArray()方法:

    public Object[] toArray() {
            return Arrays.copyOf(elementData, size);
        }
        //调用Arrays工具类的复制方法,传进对象数组复制为一个新数组,新数组的长度为原数组的元素个数size
    
  • get()方法:

     public E get(int index) {
            Objects.checkIndex(index, size);      //检查传入的参数index,是否在0到size范围内
            return elementData(index);            //返回对象数组中索引为index的元素
        }
    
  • set()方法:

    public E set(int index, E element) {
            Objects.checkIndex(index, size);       //检查传入的参数index,是否在0到size范围内
            E oldValue = elementData(index); 	   //旧值为数组对象中索引为index的值
            elementData[index] = element;		   //将传入的新值传入数组中index索引的位置
            return oldValue;					   //返回旧值
        }
    
  • remove()方法:

     public E remove(int index) {
            Objects.checkIndex(index, size);		//检测 index 是否越界
            final Object[] es = elementData;
    		//记录该索引的原值
            @SuppressWarnings("unchecked") E oldValue = (E) es[index];
            fastRemove(es, index);   //快速移除
    
            return oldValue;
        }
        
        
        
    =====================================================================================    
        
        
        
         private void fastRemove(Object[] es, int i) {
            modCount++; 		//增加修改次数
           //如果i不是最末尾的元素,则将i+1位置的数组往前挪
            final int newSize;
            if ((newSize = size - 1) > i)    //size-1的原因是:size是从1开始数,而索引下标从0开始
                System.arraycopy(es, i + 1, es, i, newSize - i);
            //将新的末尾元素变为null
            es[size = newSize] = null;
        } 
    
    • public boolean remove(Object o) {
              final Object[] es = elementData;
              final int size = this.size;
              //找首个元素为O的位置
              int i = 0;
              found: {
                  if (o == null) {   				 //如果o为Null
                      for (; i < size; i++)		//遍历数组
                          if (es[i] == null)
                              break found;
                  } else {
                      for (; i < size; i++)
                          if (o.equals(es[i]))
                              break found;
                  }
                  return false;				//如果没找到返回false
              }
              fastRemove(es, i);    			//快速移除
              return true;					//找到了返回true
          }
      
  • hashCode()方法:

    public int hashCode() {
    		//得到当前数组的修改次数
            int expectedModCount = modCount;
            //计算哈希值
            int hash = hashCodeRange(0, size);
            //如果修改次数发生变化,则抛出ConcurrentModificationException异常
            checkForComodification(expectedModCount);
            return hash;
        }
    
    
    
    
        int hashCodeRange(int from, int to) {
            final Object[] es = elementData;
            //如果元素个数超过数组长度,抛出ConcurrentModificationException异常
            if (to > es.length) {
                throw new ConcurrentModificationException();
            }
            //遍历每一个元素 *31求哈希值
            int hashCode = 1;
            for (int i = from; i < to; i++) {
                Object e = es[i];
                hashCode = 31 * hashCode + (e == null ? 0 : e.hashCode());
            }
            return hashCode;
        }
    
    • 选择*31的原因:
      • 选的太小容易哈希冲突,太大的话容易导致Int类型的溢出
      • 使用31可以使用移位运算进行简化,效率高:31*i==( i < < 5 ) - i
  • clear()方法:

     public void clear() {
            modCount++;
            final Object[] es = elementData;
            for (int to = size, i = size = 0; i < to; i++)
                es[i] = null;
                //先将数组元素个数清零,再将每一个索引位置赋值为null
        }
    
  • equals方法:

    public boolean equals(Object o) {
    			//如果元素就是本身,返回true相等
                if (o == this) {
                    return true;
                }
    			
    			//如果不为List类型  直接返回false不相等
                if (!(o instanceof List)) {
                    return false;
                }
    
                boolean equal = root.equalsRange((List<?>)o, offset, offset + size);
                checkForComodification();		//检测modCount是否被更改过,如果被更改贼抛出并发修改异常
                return equal;
            }
       
       
       
       
       
       
            
      boolean equalsRange(List<?> other, int from, int to) {
            final Object[] es = elementData;
            if (to > es.length) {
                throw new ConcurrentModificationException();
                //如果to 大于 es的大小,这说明发生了改变,抛出并发修改异常
            }
            
            //通过迭代器遍历 other 然后逐个进行比对
            var oit = other.iterator();
            for (; from < to; from++) {
            //如果 oit没有下一个,或者元素不相等,返回false不匹配
                if (!oit.hasNext() || !Objects.equals(es[from], oit.next())) {
                    return false;
                }
            }
            //通过检测oit是否遍历完成判断大小是否相等
            return !oit.hasNext();
        }       
      
      
      //Objects的equals方法:
       public static boolean equals(Object a, Object b) {
            return (a == b) || (a != null && a.equals(b));
        }
      
      
    

ArrayList源码及方法解析

上一篇:TCP/IP协议


下一篇:C#中的多态性