Java源码解读之util.ArrayList

来源:互联网 发布:苏州大数据培训课程 编辑:程序博客网 时间:2024/05/21 10:41
<script type="text/javascript">google_ad_client = "pub-8800625213955058";/* 336x280, 创建于 07-11-21 */google_ad_slot = "0989131976";google_ad_width = 336;google_ad_height = 280;//</script><script type="text/javascript"src="http://pagead2.googlesyndication.com/pagead/show_ads.js"></script>ArrayList是List接口的一个可变长数组实现。实现了所有List接口的操作,并允许存储null值。除了没有进行同步,ArrayList基本等同于Vector。在Vector中几乎对所有的方法都进行了同步,但ArrayList仅对writeObject和readObject进行了同步,其它比如add(Object)、remove(int)等都没有同步。 1.存储  ArrayList使用一个Object的数组存储元素。private transient Object elementData[];   ArrayList实现了java.io.Serializable接口,这儿的transient标示这个属性不需要自动序列化。下面会在writeObject()方法中详细讲解为什么要这样作。  2.add和removepublic boolean add(Object o) { ensureCapacity(size 1);  // Increments modCount!! elementData[size ] = o; return true;}   注意这儿的ensureCapacity()方法,它的作用是保证elementData数组的长度可以容纳一个新元素。在“自动变长机制”中将详细讲解。public Object remove(int index){ RangeCheck(index);  modCount ;  Object oldValue = elementData[index]; int numMoved = size - index - 1; if (numMoved > 0) System.arraycopy(elementData, index 1, elementData, index, numMoved);  elementData[--size] = null; // Let gc do its work return oldValue;}   RangeCheck()的作用是进行边界检查。由于ArrayList采用一个对象数组存储元素,所以在删除一个元素时需要把后面的元素前移。删除一个元素时只是把该元素在elementData数组中的引用置为null,具体的对象的销毁由垃圾收集器负责。  modCount的作用将在下面的“iterator()中的同步”中说明。  注:在前移时使用了System提供的一个实用方法:arraycopy(),在本例中可以看出System.arraycopy()方法可以对同一个数组进行操作,这个方法是一个native方法,如果对同一个数组进行操作时,会首先把从源部分拷贝到一个临时数组,在把临时数组的元素拷贝到目标位置。  3.自动变长机制  在实例化一个ArrayList时,你可以指定一个初始容量。这个容量就是elementData数组的初始长度。如果你使用:ArrayList list = new ArrayList();   则使用缺省的容量:10。public ArrayList() { this(10); }   ArrayList提供了四种add()方法,public boolean add(Object o)public void add(int index, Object element)public boolean addAll(Collection c)public boolean addAll(int index, Collection c)   在每一种add()方法中,都首先调用了一个ensureCapacity(int miniCapacity)方法,这个方法保证elementData数组的长度不小于miniCapacity。ArrayList的自动变长机制就是在这个方法中实现的。public void ensureCapacity(int minCapacity) { modCount ;  int oldCapacity = elementData.length;  if (minCapacity > oldCapacity) {  Object oldData[] = elementData;  int newCapacity = (oldCapacity * 3)/2 1;  if (newCapacity < minCapacity) newCapacity = minCapacity;   elementData = new Object[newCapacity];  System.arraycopy(oldData, 0, elementData, 0, size);  }}   从这个方法实现中可以看出ArrayList每次扩容,都扩大到原来大小的1.5倍。  每种add()方法的实现都大同小异,下面给出add(Object)方法的实现:public boolean add(Object o) {  ensureCapacity(size 1); // Increments modCount!! elementData[size ] = o;  return true;}  4.iterator()中的同步  在父类AbstractList中定义了一个int型的属性:modCount,记录了ArrayList结构性变化的次数。protected transient int modCount = 0;   在ArrayList的所有涉及结构变化的方法中都增加modCount的值,包括:add()、remove()、addAll()、removeRange()及clear()方法。这些方法每调用一次,modCount的值就加1。  注:add()及addAll()方法的modCount的值是在其中调用的ensureCapacity()方法中增加的。  AbstractList中的iterator()方法(ArrayList直接继承了这个方法)使用了一个私有内部成员类Itr,生成一个Itr对象(Iterator接口)返回:public Iterator iterator() { return new Itr(); }   Itr实现了Iterator()接口,其中也定义了一个int型的属性:expectedModCount,这个属性在Itr类初始化时被赋予ArrayList对象的modCount属性的值。int expectedModCount = modCount;   注:内部成员类Itr也是ArrayList类的一个成员,它可以访问所有的AbstractList的属性和方法。理解了这一点,Itr类的实现就容易理解了。  在Itr.hasNext()方法中:public boolean hasNext() { return cursor != size(); }   调用了AbstractList的size()方法,比较当前光标位置是否越界。  在Itr.next()方法中,Itr也调用了定义在AbstractList中的get(int)方法,返回当前光标处的元素:public Object next(){ try  {  Object next = get(cursor);   checkForComodification();   lastRet = cursor ;  return next; } catch(IndexOutOfBoundsException e)  {   checkForComodification();   throw new NoSuchElementException(); }}   注意,在next()方法中调用了checkForComodification()方法,进行对修改的同步检查:final void checkForComodification() { if (modCount != expectedModCount) throw new ConcurrentModificationException(); }   现在对modCount和expectedModCount的作用应该非常清楚了。在对一个集合对象进行跌代操作的同时,并不限制对集合对象的元素进行操作,这些操作包括一些可能引起跌代错误的add()或remove()等危险操作。在AbstractList中,使用了一个简单的机制来规避这些风险。这就是modCount和expectedModCount的作用所在。  5.序列化支持  ArrayList实现了java.io.Serializable接口,所以ArrayList对象可以序列化到持久存储介质中。 ArrayList的主要属性定义如下:private static final long serialVersionUID = 8683452581122892189L;private transient Object elementData[];private int size;   可以看出serialVersionUID和size都将自动序列化到介质中,但elementData数组对象却定义为transient了。也就是说ArrayList中的所有这些元素都不会自动系列化到介质中。为什么要这样实现?因为elementData数组中存储的“元素”其实仅是对这些元素的一个引用,并不是真正的对象,序列化一个对象的引用是毫无意义的,因为序列化是为了反序列化,当你反序列化时,这些对象的引用已经不可能指向原来的对象了。所以在这儿需要手工的对ArrayList的元素进行序列化操作。这就是writeObject()的作用。private synchronized void writeObject(java.io.ObjectOutputStream s) throws java.io.IOException{// Write out element count, and any hidden stuff s.defaultWriteObject();// Write out array length s.writeInt(elementData.length);// Write out all elements in the proper order. for (int i=0; i<size; i ) s.writeObject(elementData[i]);}   这样元素数组elementData中的所以元素对象就可以正确地序列化到存储介质了。  对应的readObject()也按照writeObject()方法的顺序从输入流中读取:private synchronized void readObject(java.io.ObjectInputStream s) throws java.io.IOException, ClassNotFoundException {// Read in size, and any hidden stuff s.defaultReadObject(); // Read in array length and allocate array int arrayLength = s.readInt();elementData = new Object[arrayLength]; // Read in all elements in the proper order.for (int i=0; i<size; i ) elementData[i] = s.readObject(); }
原创粉丝点击
热门IT博客
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 液压手拉车 液压拉车 液压小车 手动叉车液压缸 电动液压车 电动液压车价格 液压装卸车 液压平板车 液压扫路车 液压滤油车 液压车价格 手动液压车价格 液压车维修 升降液压车 半自动液压车 液压车图片 诺力手动液压车 小型液压升降车 液压车油封 液压移车器 液压举升车 手推液压搬运车 液压车原理 液压式阻车器 液压搬运车图片 油压车图片 搅拌车液压系统 液压搬运车原理 汽车油压表 汽车油压电磁阀 液压钳子价格 电动液压钳子 液压钳图片 液压电缆钳 液压破碎钳 液压钳多少钱一把 电工液压钳 破碎液压钳 电动液压钳图片及价格 液压接线钳 液压钢筋钳