在我们的日常开发工作当中,集合用到的次数非常多,常用的List集合有ArrayList, LindedList等等,今天我们从源码的角度来分析一下ArrayList的工作原理,搞清楚它 的内部是如何存储数据,如何工作的。
1:我们要了解它的原理,首先从如何使用来看,通过new产生一个实例;如下:
List list = new ArrayList(); 我们点击进去,看它的构造里面是什么?
咦?array是什么鬼?点击一下
原来它是一个Object数组,就是说ArrayList的数据很有可能是保存在这个array的数组当中的,我们再次回到他的构造方法,array = EmptyArray.OBJECT;
EmptyArray.OBJECT; ??? 这是什么捏?让我们来查看下这个EmptyArray类。
这下就清晰了,它其实是引用了一个大小为0的Object 数组对象。
我们再来看一下它的其他构造:
我们看到当capacity == 0 ?的时候和上一个构造逻辑是一样的,如果大于0的话就直接new了一个capacity大小的Object 数组对象。
collection参数是一个集合,首先转换集合为Object[];
判断如果不等于Object[].class;重新new一个大小相等的Object数组, 然后把a数组里的数据全部copy到newArray数组里面;再把a的引用指给newArray。
如果等于的话直接滤过上面的过程;
然后把array的引用指向a,并且赋值size字段。
至此所有构造方法大家都了解了;下一步我们来看看它的add方法。
2:我们先来看看它的所有add方法
可以看出总共有4个add方法,让我们一个一个来跟踪;
2.1:第一个add(Object object)
首先把array赋值给a数组;把size(表示当前添加了多少数据的大小)变量赋值给s。
然后判断如果s == a.length,如果当前已添数据的大小已经和a数组的大小相同了,已经无法再添加数据了,这个时候需要扩充数组大小,来容纳新添加的数据;
首先new一个新的数组,重要是看数组的大小。大小等于它当前的大小s加上后面的逻辑判断,逻辑判断:这里有一个最小容量MIN_CAPACITY_INCREMENT=12,如果当前的大小小于最小容量的一半,那就用当前的大小s+最小容量,组合新的数组大小;反之如果大于最小容量的一半;那就用当前的大小s+(s>>1 ps : 就是s/2);然后copy原来数组中的数据到新的数组当中,再把新数组newArray赋值给a赋值给array;
然后设置a[s] = object;把新数据添加到数组中,然后改变size大小。
2.2:第二个add(int index,E object),在第index个位置添加object数据;
挑重点看;
if(s < a.length) {
System.arraycopy(a,index,a,index +1,s - index);
}
上面的代码意思:
如果当前添加数据的数量小于数组的总长度;就是说如果数组的总长度是10,但是我们当前只添加了5条数据;然后进行copy数据,其实就是数据的移动;
从a数组中的index位置开始copy,copy的长度为s-index,还是copy到a数组,从index+1的位置开始copy,通俗一点讲就是:把index位置的数据以及后面的数据通通往后移一位;然后把新添加的数据添加到index的位置。
反之请看:
Object[] newArray =newObject[newCapacity(s)];
System.arraycopy(a,0,newArray,0,index);
System.arraycopy(a,index,newArray,index +1,s - index);
array= a = newArray;
重新new一个Object数组,数组的大小依据上一个add方法的逻辑判断;然后把数组a当中的数据从0开始copy,copy的长度为index,copy到newArray当中;然后再把从index之后的数据copy到newArray当中去,从index+1的位置开始copy,剩下就是把要添加的数据object设置到index的位置。
用个图来看下可能会清晰一点,好理解;
如果index=2,两个copy方法就分别把1和2 copy到index的前面,然后把3,4,5 copy到index 的后面;
然后把要添加的Object赋值给index位置。
2.3:addAll(Collection collection) 添加一个集合到list
和上面2个逻辑差不多;把新集合的大小和当前list的大小加到一起判断;
如果大于当前list的大小,就new一个新的数组,进行数据copy;
反之就是把新集合的数据copy到当前list当中,从最后位置开始copy。
2.4:addAll(int index,Collection collection) 从指定的index位置添加集合;
和2.2的逻辑差不多,就是移动的位置由1位变成了新集合的长度。
到此位置,list的add方法都已经分析完成了。
可以看出来ArrayList其实就是对数组的操作,在添加的过程中,不断的改变数组的大小来适应数据量,所以我们以后使用ArrayList的时候如果已经得知要添加的数据量,最好使用可以设置数组大小的那个构造方法,这样可以减少重新创建数组和copy数据,优化性能。
下一篇文章我们来看看ArrayList的其他方法