集合源码分析(一)ArrayList集合

1、ArrayList 概述

ArrayList 是 Collection 单例集合体系中使用率最多的一个集合。是 List 的第一个实现类,全面替代了 Vector 。底层是数组结构的.
特点:
    有序、有索引、可以重复。

基本使用:
    ArrayList<Integer> al = new ArrayList<>();
        al.add(1);
        al.add(2);
        al.add(3);

2、ArrayList 的四种遍历方式:

2.1、增强for

   //i只是其中的一个变量,更改i的值,不会更改集合中的数据

  for (Integer i : al) {
       System.out.println(i);
  }

2.2、迭代器

   //在迭代的时候不能利用集合的方法改变集合的长度。会报并发修改异常

Iterator<Integer> it = al.iterator();
while(it.hasNext()){
      Integer next = it.next();
      System.out.println(next);
}


2.3、普通for

//在删除数据的时候,要i--。否则会导致相邻的两个数不能删除

for (int i = 0; i < al.size(); i++) {
    Integer ii = al.get(i);
    System.out.println(ii);
}

2.4、转数组

Object[] array = al.toArray(); //toArray()没有传递参数,所以是object类型的
for (Object o : array) {
    System.out.println(o);
}

Integer[] array = al.toArray(new Integer[0]);//传递一个T类型的数组,那么返回值就是T类型的。
                                    //此处数组长度与集合长度比,谁长听谁的,不足null补齐。
                                    //所以可以写0或者是集合的size
for (Integer i : array) {                    
    System.out.println(i);
}

3、方法解析:

3.1、无参构造

ArrayList<Integer> al = new ArrayList<>();
public ArrayList() {
    super();
this.elementData = EMPTY_ELEMENTDATA;
//elementData 是ArrayList底层实际存值的数组
//EMPTY_ELEMENTDATA 是一个object类型的空数组
//创建对象的时候ArrayList就是一个空数组
}

3.2、add():

void add(int index, E element):将指定的元素插入此列表中的指定位置。
 //如果集合中最大索引是3,那么调用此方法,可以在4索引插入元素。但是超过4索引就报错了。

boolean add(Object e): 向集合中添加元素。

//方法源码分析:

public boolean add(E e) {
    ensureCapacityInternal(size + 1);//重新计算长度,并让modCount++
				//modCount是并发修改异常中的关键,下面会解释
    elementData[size++] = e;//每次往数组的末尾存。保证了存取有序
    return true;	//因为ArrayList可重复所以每次都是true。跟HashSet相对
} 

//-----------------------	
private void ensureCapacityInternal(int minCapacity) {
if (elementData == EMPTY_ELEMENTDATA) {
    minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);//默认大小跟实际大小比较
}//保证了ArrayList只要刚开始添加就有一个默认10大小的容量,因为DEFAULT_CAPACITY = 10;
							ensureExplicitCapacity(minCapacity);
}

//------------------------
private void ensureExplicitCapacity(int minCapacity) {
    modCount++;//modCount是并发修改异常中的关键,下面会解释
    if (minCapacity - elementData.length > 0)//比较长度
	grow(minCapacity);
}

//------------------------
private void grow(int minCapacity) {
    int oldCapacity = elementData.length;//旧容量
    int newCapacity = oldCapacity + (oldCapacity >> 1);//新容量 = 旧容量 + 旧容量/2
    if (newCapacity - minCapacity < 0)
	newCapacity = minCapacity;
    if (newCapacity - MAX_ARRAY_SIZE > 0)
	newCapacity = hugeCapacity(minCapacity);
	elementData = Arrays.copyOf(elementData, newCapacity);//数组的拷贝//底层调用System.arraycopy拷贝
}

    综上所述:ArrayList 底层是一个长度为0的数组。在刚添加一个元素后,变成默认大小10。
              只要是存满了。那么就把原来的长度*1.5(也就是扩容原先的1.5倍大小。)
              再创建了一个新的数组,新的数组的长度为原来的1.5倍
              利用 System.arraycopy 的方法将原数组的内容拷贝到新的数组中去。

3.3、clear():

void clear():清空集合中所有元素。
 //方法源码分析                

for (int i = 0; i < size; i++)  //遍历集合
    elementData[i] = null;   //得到每一个元素,把每一个元素变成null
    size = 0;                //将长度变为0

3.4、contain():

boolean contains(Object o):判断集合中是否包含某个元素。
//方法源码分析

public boolean contains(Object o) {
     return indexOf(o) >= 0;//调用indexOf看参数在集合中是多少索引。
                            //如果索引为正数,那么集合中就包含参数
}

//------------------------
public int indexOf(Object o) {
    if (o == null) {        //判断参数是否为null
    for (int i = 0; i < size; i++)//遍历
    if (elementData[i]==null)//判断遍历到的是否为null
        return i;            //如果存在就直接return
    } else {
        for (int i = 0; i < size; i++)    //遍历
            if (o.equals(elementData[i])) //利用equals进行判断
               return i;                  //如果存在就直接return
             }
        return -1;//如果不存在就return-1
}

                    
    综上所述:"方法底层是依赖 equals" 方法来判断是否是否与集合内部某一个元素相同。
              如果我们没有重写equals。那么用的是object里面的equals。此时判断的是地址值。
              当遇到一个匹配的直接return,所以当重复时候,只能返回第一个。

3.5、isEmpty():

boolean isEmpty():判断集合中的元素是否为空

//方法源码分析                         

 public boolean isEmpty() {
       return size == 0;//就是判断长度是否为0
 }

3.6、remove(Object o):

boolean remove(Object o):根据元素的内容来删除某个元素

//方法源码分析    

public boolean remove(Object o) {
	if (o == null) {//判断参数是否为null
		for (int index = 0; index < size; index++)//遍历
			if (elementData[index] == null) {
				fastRemove(index);	//调用另外的方法删除。
				return true;//只要成功删除一个,就直接return了。
			}
	} else {
		for (int index = 0; index < size; index++)//遍历
			if (o.equals(elementData[index])) {"方法底层是依赖 equals"
				fastRemove(index);//调用另外的方法删除。
				return true;//只要成功删除一个,就直接return了。
			}
	}
	return false;
}

//--------------------------------------------------------------------
 private void fastRemove(int index) {
	modCount++; //重新计算长度,并让modCount++
				//modCount是并发修改异常中的关键,下面会解释
	int numMoved = size - index - 1;//表示要删除的元素右边还有几个
	if (numMoved > 0)
		System.arraycopy(elementData, index+1, elementData, index,
						 numMoved);//把数组复制给自己。
						//从要删除的后一个开始复制给自己
						//复制到要删除的索引开始,复制numMoved个。
	elementData[--size] = null; 
}

终上所述:"方法底层是依赖 equals" 方法来判断是否是否与集合内部某一个元素相同。

              如果我们没有重写equals。那么用的是object里面的equals。此时判断的是地址值。
              当遇到一个匹配的直接return,所以当重复时候,只能删除第一个。
              删除完毕,数组长度--。调用System.arraycopy拷贝给自己。

注意点:
        boolean remove(Object o)有一个重载的方法
        E remove(int index)
        在ArrayList<Integer> al = new ArrayList<>();这样创建对象
            用al调用remove.传递2
            al.remove(2);//此处的2不会自动装箱。删除的是2索引
        但是如果 Collection<Integer> al = new ArrayList<>();这样创建对象
            用al调用remove.传递2
            al.remove(2);//多态创建
            编译看左边,Collection中只有remove(Object o)的方法
            所以右边会走remove(Object o)


3.7、size():

int size():获取集合的长度

//方法源码分析    

public int size() {
          return size;//实际上就是数组的长度

}

3.8、toArray():

Object[] toArray():能够将集合转换成数组并把集合中的元素存储到数组中

//方法源码分析   

public Object[] toArray() {
	return Arrays.copyOf(elementData, size);
}
//-------------------------------------------
public static <T,U> T[] copyOf(U[] original, int newLength, Class<? extends T[]> newType) {
		// original 要拷贝的数组  newLength 集合的长度,也就是转成数组后数组的长度
	T[] copy = ((Object)newType == (Object)Object[].class)? 
		(T[]) new Object[newLength]//创建一个Object类型的数组,再转换成T类型的数组。
			//T就是toArray()重载方法toArray(T[] a)传递的参数
		: (T[]) Array.newInstance(newType.getComponentType(), newLength);
	System.arraycopy(original, 0, copy, 0,Math.min(original.length, newLength));
			//数组的复制
	return copy;//将创建的数组返回。
}	

终上所述:如果是 toArray()就创建了一个新的Object类型的数组。
              如果有参数toArray(new String[0])就创建了一个新的Object类型的数组再转成String类型
              利用System.arraycopy的方法,将元素拷贝到数组中去。再将新数组返回

3.9、toArray(T[] a):

<T> T[] toArray(T[] a):能够将集合转换成数组并把集合中的元素存储到数组中

//方法源码分析    

public <T> T[] toArray(T[] a) {
	if (a.length < size)//如果参数数组长度小于集合长度
		return (T[]) Arrays.copyOf(elementData, size, a.getClass());//按照集合长度进行拷贝
	System.arraycopy(elementData, 0, a, 0, size);//如果参数数组长度大于集合长度直接拷贝
	if (a.length > size)//将过于的赋值为null
		a[size] = null;//引用数据类型默认null。所以不赋值也是可以的,就是初始化值
	return a;//将数组返回
}	

终上所述:如果有参数toArray(new String[0])
                先判断0跟集合长度的大小。如果集合长度大,按照集合长度进行拷贝,创建一个新的Object类型的数组再转成String类型
                利用System.arraycopy的方法,将元素拷贝到数组中去。再将新数组返回
                如果一样长,直接拷贝
                如果参数数组长度大,那么后面的赋值为null。因为集合是引用数据类型。
                所以直接是默认初始化值
              "从效率而言,此处toArray(new String[list.size()])"
              参数大小设置为集合长度最合适。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值