最近有点空闲的时间,就对ArrayList进行了源码的查看。深思当ArrayList存在临界值时,该作者做了哪些操作.顺便记录下自己在源码中所了解的逻辑.这次博文入口显示添加和删除。
看过源码的都知道,ArrayList在初始化没有给定容器大小的时候,他会自动的帮我们创建一个10的集合大小.就是这段,他是在调用
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
}
添加方法的时候,会判断是否是默认的共享实例 官方解释(/** * Shared empty array instance used for default sized empty instances. We * distinguish this from EMPTY_ELEMENTDATA to know how much to inflate when * first element is added. */)
程序发现是通过空的构造函数之后,在通过与当前的size+1的大小进行对比,通过max函数取最大的值,进行初始化容量.在确认过容量之后,此时的object对象还是一个空的无容量的集合,那么接下来就是对该空的数组赋容量.ensureExplicitCapacity调用改方法.
if (minCapacity - elementData.length > 0)
grow(minCapacity);
在该方法中该作者做了一个为防止溢出而做的一个过滤,那么什么情况下会出现这种情况呢。我们就给出一个int的最大值,假设这个数组的最大容量就是这个int的所能承受的最大值,那么此时改ArrayList在添加一个元素的时候,就是在最大值的时候加1也就是一个负数.带这个问题,我们来看下这个grow方法里面的状况.这个里面做的一些扩容的事情.那么我们就带着刚刚的那个不加过滤的问题,会出现什么情况.
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 newCapacity = oldCapacity + (oldCapacity >> 1)这个时;他的大小就是多少呢>>1其实就是一个二进制的一个右移动的一个过程然后在转10进制.具体的大家可以看下我这篇博文https://blog.csdn.net/ArryLuo123/article/details/81224930,那么当int进行移位了之后,再加上int的最大值,可想而知,他肯定就是一个负数。if (newCapacity - minCapacity < 0)一个绝对值大于一个添加的元素的绝对值时,两个数相减一定是一个负数,故,他就会赋值newCapacity = minCapacity;那么就会执行elementData = Arrays.copyOf(elementData, newCapacity);也就是newCapacity这个值就是一个负数的一个拷贝,故会出现这个异常java.lang.NegativeArraySizeException,那么接下来就是newCapacity - MAX_ARRAY_SIZE > 0这种情况了,这种情况是一种临界点,也就是int最大值和int最大值-8的一个相减而大于0的一种情况,其实也是一种溢出校验。通过层层把关之后,在调用elementData[size++] = e;进行赋值操作.
下面我们通过手写一个ArrayListdemo进行描述下,本demo不校验他们临界值以及各种情况,
/**
* @author arryluo
* 手写一个ArrayList
* //一个简单的仿ArrayList的一个添加操作。不涉及到临界溢出异常的处理,以及对数据是否合法进行校验,
*/
public class MyArryayList {
int size=0;//这个是记录真实的数据容量大小,而不是数组的容量大小
Object[]elementData={};//创建一个空的数组
private static final int DEFAULT_CAPACITY = 10;
public MyArryayList(){
elementData= Arrays.copyOf(elementData, DEFAULT_CAPACITY);
}
public void add(Object o){
elementData[size++]=o;
}
public int size(){
return size;
}
//取出数据,不检测对该输入的角标是否大于真实数据的容量大小.
public Object get(int index){
return elementData[index];
}
}
进行调用
public static void main(String[] args) {
MyArryayList myArryayList=new MyArryayList();
myArryayList.add("哈哈");
System.out.println("真实容量:"+myArryayList.size());
//进行遍历
for (int i = 0; i <myArryayList.size() ; i++) {
Object object= myArryayList.get(i);
System.out.println(object);
}
}
执行结果
Connected to the target VM, address: '127.0.0.1:3141', transport: 'socket'
真实容量:1
哈哈
Disconnected from the target VM, address: '127.0.0.1:3141', transport: 'socket'
Process finished with exit code 0
针对为什么源码中给出的集合大小不是数组的真实大小。白话文,主要还是在遍历集合的时候,我们操作集合的数据以及大小的时候,如果用真实的,不便进行计算以及取值.同时也不便在添加数据的时候,进行一个扩容处理,具体可以往上面描述的进行查看,官方解释( minCapacity is usually close to size, so this is a win:).
流程图描述