【源代码】java.util.ArrayList

与LinkedList类似,ArrayList是另一个线性表容器。不过其内部基于数组的扩容实现。本文罗列其源代码并分析其原理。


ArrayList类型声明

public class ArrayList extends AbstractList
实现接口声明:

implements List, RandomAccess, Cloneable, Serializable

成员变量:

 
  
private static final long serialVersionUID = 8683452581122892189L;
private static final int DEFAULT_CAPACITY = 10;
private int size;
private transient E[] data;
其中定义序列化ID,默认存储长度DEFAULT_CAPACITY为10,当前长度size,最后一个是存储的基础:一个泛型数组data。
 
  
构造函数有三个,分别为:
1.无参构造,默认用Default_capacity去初始化数组。
 
  
 125:   public ArrayList()
 126:   {
 127:     this(DEFAULT_CAPACITY);
 128:   }
2.以制定长度为参,初始化数组data的长度。
 
  
 114:   public ArrayList(int capacity)
 115:   {
 116:     // Must explicitly check, to get correct exception.
 117:     if (capacity < 0)
 118:       throw new IllegalArgumentException();
 119:     data = (E[]) new Object[capacity];
 120:   }
3.以容器类为参:
 
  
 138:   public ArrayList(Collection c)
 139:   {
 140:     this((int) (c.size() * 1.1f));
 141:     addAll(c);
 142:   }

接下来定义了几个灵活控制数组data长度的方法:
1.压缩当前数组到制定长度:
 
  
 148:   public void trimToSize()
 149:   {
 150:     // Not a structural change from the perspective of iterators on this list,
 151:     // so don't update modCount.
 152:     if (size != data.length)
 153:       {
 154:         E[] newData = (E[]) new Object[size];
 155:         System.arraycopy(data, 0, newData, 0, size);
 156:         data = newData;
 157:       }
 158:   }
其中使用了System.arraycopy()方法。这里是数组复制方法的官方定义:
 
  
public static void arraycopy(Object src,
             int srcPos,
             Object dest,
             int destPos,
             int length)
该方法是将一个指定的数组,从一个特定的位置开始复制到一个目标数组中。
(参考: http://docs.oracle.com/javase/7/docs/api/java/lang/System.html)
那么trimToSize()方法其实是发现当前元素个数size比data数组的长度小时,初始化一个新的长度为size的数组并作为目标数组将data复制到其中作为新的data数组。方法定义很巧妙使用了Object数组的类型强转,很有用的编程技巧。
 
  
那么如果size大于data.length了该如何办呢。ArrayList还定义了扩充数组长度的方法:
 
  
 170:   public void ensureCapacity(int minCapacity)
 171:   {
 172:     int current = data.length;
 173: 
 174:     if (minCapacity > current)
 175:       {
 176:         E[] newData = (E[]) new Object[Math.max(current * 2, minCapacity)];
 177:         System.arraycopy(data, 0, newData, 0, size);
 178:         data = newData;
 179:       }
 180:   }
注意,上述两个调整数组data大小的方法都是public,因此我们可以在需要的时候手动调用。当你发现存储的数据过多时,虽然ArrayList会自动扩充,但是它的操作非常的繁琐和低效,可能对于一些特性需求时,显得有些耗时(之后我会展示它自动扩充的操作)。那么我们完全可以手动操作数组的扩充来避免ArrayList的反复扩充和复制操作。
 
  
ArrayList实现了克隆接口,所以克隆操作定义如下:
 
  
 248:   public Object clone()
 249:   {
 250:     ArrayList clone = null;
 251:     try
 252:       {
 253:         clone = (ArrayList) super.clone();
 254:         clone.data = (E[]) data.clone();
 255:       }
 256:     catch (CloneNotSupportedException e)
 257:       {
 258:         // Impossible to get here.
 259:       }
 260:     return clone;
 261:   }
有的时候如果需要将ArrayList转换为数组,这里提供了两个成员方法: 
  
 
  
 269:   public Object[] toArray()
 270:   {
 271:     E[] array = (E[]) new Object[size];
 272:     System.arraycopy(data, 0, array, 0, size);
 273:     return array;
 274:   }
 290:   public  T[] toArray(T[] a)
 291:   {
 292:     if (a.length < size)
 293:       a = (T[]) Array.newInstance(a.getClass().getComponentType(), size);
 294:     else if (a.length > size)
 295:       a[size] = null;
 296:     System.arraycopy(data, 0, a, 0, size);
 297:     return a;
 298:   }
对于移除元素操作,这里省略了基本的remove等操作,但是ArrayList里边有一个比较特殊的方法: 
  
 
  
 450:   protected void removeRange(int fromIndex, int toIndex)
 451:   {
 452:     int change = toIndex - fromIndex;
 453:     if (change > 0)
 454:       {
 455:         modCount++;
 456:         System.arraycopy(data, toIndex, data, fromIndex, size - toIndex);
 457:         size -= change;
 458:       }
 459:     else if (change < 0)
 460:       throw new IndexOutOfBoundsException();
 461:   }
这是一个按范围移除元素的操作。其中同样适用了System.arraycopy()方法,同时附带有对输入索引合法性的检测。

另外移除操作中还有一个也很特殊的方法:
 
  
 504:   boolean removeAllInternal(Collection c)
 505:   {
 506:     int i;
 507:     int j;
 508:     for (i = 0; i < size; i++)
 509:       if (c.contains(data[i]))
 510:         break;
 511:     if (i == size)
 512:       return false;
 513: 
 514:     modCount++;
 515:     for (j = i++; i < size; i++)
 516:       if (! c.contains(data[i]))
 517:         data[j++] = data[i];
 518:     size -= i - j;
 519:     return true;
 520:   }
这是根据输入容器来移除数组data中共同的元素。其中使用了contains()方法,其定义如下:
 
  
 208:   public boolean contains(Object e)
 209:   {
 210:     return indexOf(e) != -1;
 211:   }
 220:   public int indexOf(Object e)
 221:   {
 222:     for (int i = 0; i < size; i++)
 223:       if (equals(e, data[i]))
 224:         return i;
 225:     return -1;
 226:   }
removeAllInternal()方法的编写值得学习,非常巧妙的将数组中的元素移除并且是一个线性时间的操作。
 
  
与此类似,还有一个是保留共有元素的操作:
 
  
 532:   boolean retainAllInternal(Collection c)
 533:   {
 534:     int i;
 535:     int j;
 536:     for (i = 0; i < size; i++)
 537:       if (! c.contains(data[i]))
 538:         break;
 539:     if (i == size)
 540:       return false;
 541: 
 542:     modCount++;
 543:     for (j = i++; i < size; i++)
 544:       if (c.contains(data[i]))
 545:         data[j++] = data[i];
 546:     size -= i - j;
 547:     return true;
 548:   }
与LinkedList不同,因为ArrayList其本质是一个数组,所以无需定义迭代器,使用索引即可。一下提供完整源代码,可以查看到一些我并没有在文中提及的常见的方法。 
  
欢迎交流指正。

源代码一览
源代码来源:http://developer.classpath.org/doc/java/util/ArrayList-source.html
 
  
   1: /* ArrayList.java -- JDK1.2's answer to Vector; this is an array-backed
   2:    implementation of the List interface
   3:    Copyright (C) 1998, 1999, 2000, 2001, 2004, 2005  Free Software Foundation, Inc.
   4: 
   5: This file is part of GNU Classpath.
   6: 
   7: GNU Classpath is free software; you can redistribute it and/or modify
   8: it under the terms of the GNU General Public License as published by
   9: the Free Software Foundation; either version 2, or (at your option)
  10: any later version.
  11: 
  12: GNU Classpath is distributed in the hope that it will be useful, but
  13: WITHOUT ANY WARRANTY; without even the implied warranty of
  14: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  15: General Public License for more details.
  16: 
  17: You should have received a copy of the GNU General Public License
  18: along with GNU Classpath; see the file COPYING.  If not, write to the
  19: Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  20: 02110-1301 USA.
  21: 
  22: Linking this library statically or dynamically with other modules is
  23: making a combined work based on this library.  Thus, the terms and
  24: conditions of the GNU General Public License cover the whole
  25: combination.
  26: 
  27: As a special exception, the copyright holders of this library give you
  28: permission to link this library with independent modules to produce an
  29: executable, regardless of the license terms of these independent
  30: modules, and to copy and distribute the resulting executable under
  31: terms of your choice, provided that you also meet, for each linked
  32: independent module, the terms and conditions of the license of that
  33: module.  An independent module is a module which is not derived from
  34: or based on this library.  If you modify this library, you may extend
  35: this exception to your version of the library, but you are not
  36: obligated to do so.  If you do not wish to do so, delete this
  37: exception statement from your version. */
  38: 
  39: 
  40: package java.util;
  41: 
  42: import java.io.IOException;
  43: import java.io.ObjectInputStream;
  44: import java.io.ObjectOutputStream;
  45: import java.io.Serializable;
  46: import java.lang.reflect.Array;
  47: 
  48: /**
  49:  * An array-backed implementation of the List interface.  This implements
  50:  * all optional list operations, and permits null elements, so that it is
  51:  * better than Vector, which it replaces. Random access is roughly constant
  52:  * time, and iteration is roughly linear time, so it is nice and fast, with
  53:  * less overhead than a LinkedList.
  54:  * 

55: * 56: * Each list has a capacity, and as the array reaches that capacity it 57: * is automatically transferred to a larger array. You also have access to 58: * ensureCapacity and trimToSize to control the backing array's size, avoiding 59: * reallocation or wasted memory. 60: *

61: * 62: * ArrayList is not synchronized, so if you need multi-threaded access, 63: * consider using:
64: * List l = Collections.synchronizedList(new ArrayList(...)); 65: *

66: * 67: * The iterators are fail-fast, meaning that any structural 68: * modification, except for remove() called on the iterator 69: * itself, cause the iterator to throw a 70: * {@link ConcurrentModificationException} rather than exhibit 71: * non-deterministic behavior. 72: * 73: * @author Jon A. Zeppieri 74: * @author Bryce McKinlay 75: * @author Eric Blake ([email protected]) 76: * @see Collection 77: * @see List 78: * @see LinkedList 79: * @see Vector 80: * @see Collections#synchronizedList(List) 81: * @see AbstractList 82: * @status updated to 1.4 83: */ 84: public class ArrayList extends AbstractList 85: implements List, RandomAccess, Cloneable, Serializable 86: { 87: /** 88: * Compatible with JDK 1.2 89: */ 90: private static final long serialVersionUID = 8683452581122892189L; 91: 92: /** 93: * The default capacity for new ArrayLists. 94: */ 95: private static final int DEFAULT_CAPACITY = 10; 96: 97: /** 98: * The number of elements in this list. 99: * @serial the list size 100: */ 101: private int size; 102: 103: /** 104: * Where the data is stored. 105: */ 106: private transient E[] data; 107: 108: /** 109: * Construct a new ArrayList with the supplied initial capacity. 110: * 111: * @param capacity initial capacity of this ArrayList 112: * @throws IllegalArgumentException if capacity is negative 113: */ 114: public ArrayList(int capacity) 115: { 116: // Must explicitly check, to get correct exception. 117: if (capacity < 0) 118: throw new IllegalArgumentException(); 119: data = (E[]) new Object[capacity]; 120: } 121: 122: /** 123: * Construct a new ArrayList with the default capacity (16). 124: */ 125: public ArrayList() 126: { 127: this(DEFAULT_CAPACITY); 128: } 129: 130: /** 131: * Construct a new ArrayList, and initialize it with the elements 132: * in the supplied Collection. The initial capacity is 110% of the 133: * Collection's size. 134: * 135: * @param c the collection whose elements will initialize this list 136: * @throws NullPointerException if c is null 137: */ 138: public ArrayList(Collection c) 139: { 140: this((int) (c.size() * 1.1f)); 141: addAll(c); 142: } 143: 144: /** 145: * Trims the capacity of this List to be equal to its size; 146: * a memory saver. 147: */ 148: public void trimToSize() 149: { 150: // Not a structural change from the perspective of iterators on this list, 151: // so don't update modCount. 152: if (size != data.length) 153: { 154: E[] newData = (E[]) new Object[size]; 155: System.arraycopy(data, 0, newData, 0, size); 156: data = newData; 157: } 158: } 159: 160: /** 161: * Guarantees that this list will have at least enough capacity to 162: * hold minCapacity elements. This implementation will grow the list to 163: * max(current * 2, minCapacity) if (minCapacity > current). The JCL says 164: * explictly that "this method increases its capacity to minCap", while 165: * the JDK 1.3 online docs specify that the list will grow to at least the 166: * size specified. 167: * 168: * @param minCapacity the minimum guaranteed capacity 169: */ 170: public void ensureCapacity(int minCapacity) 171: { 172: int current = data.length; 173: 174: if (minCapacity > current) 175: { 176: E[] newData = (E[]) new Object[Math.max(current * 2, minCapacity)]; 177: System.arraycopy(data, 0, newData, 0, size); 178: data = newData; 179: } 180: } 181: 182: /** 183: * Returns the number of elements in this list. 184: * 185: * @return the list size 186: */ 187: public int size() 188: { 189: return size; 190: } 191: 192: /** 193: * Checks if the list is empty. 194: * 195: * @return true if there are no elements 196: */ 197: public boolean isEmpty() 198: { 199: return size == 0; 200: } 201: 202: /** 203: * Returns true iff element is in this ArrayList. 204: * 205: * @param e the element whose inclusion in the List is being tested 206: * @return true if the list contains e 207: */ 208: public boolean contains(Object e) 209: { 210: return indexOf(e) != -1; 211: } 212: 213: /** 214: * Returns the lowest index at which element appears in this List, or 215: * -1 if it does not appear. 216: * 217: * @param e the element whose inclusion in the List is being tested 218: * @return the index where e was found 219: */ 220: public int indexOf(Object e) 221: { 222: for (int i = 0; i < size; i++) 223: if (equals(e, data[i])) 224: return i; 225: return -1; 226: } 227: 228: /** 229: * Returns the highest index at which element appears in this List, or 230: * -1 if it does not appear. 231: * 232: * @param e the element whose inclusion in the List is being tested 233: * @return the index where e was found 234: */ 235: public int lastIndexOf(Object e) 236: { 237: for (int i = size - 1; i >= 0; i--) 238: if (equals(e, data[i])) 239: return i; 240: return -1; 241: } 242: 243: /** 244: * Creates a shallow copy of this ArrayList (elements are not cloned). 245: * 246: * @return the cloned object 247: */ 248: public Object clone() 249: { 250: ArrayList clone = null; 251: try 252: { 253: clone = (ArrayList) super.clone(); 254: clone.data = (E[]) data.clone(); 255: } 256: catch (CloneNotSupportedException e) 257: { 258: // Impossible to get here. 259: } 260: return clone; 261: } 262: 263: /** 264: * Returns an Object array containing all of the elements in this ArrayList. 265: * The array is independent of this list. 266: * 267: * @return an array representation of this list 268: */ 269: public Object[] toArray() 270: { 271: E[] array = (E[]) new Object[size]; 272: System.arraycopy(data, 0, array, 0, size); 273: return array; 274: } 275: 276: /** 277: * Returns an Array whose component type is the runtime component type of 278: * the passed-in Array. The returned Array is populated with all of the 279: * elements in this ArrayList. If the passed-in Array is not large enough 280: * to store all of the elements in this List, a new Array will be created 281: * and returned; if the passed-in Array is larger than the size 282: * of this List, then size() index will be set to null. 283: * 284: * @param a the passed-in Array 285: * @return an array representation of this list 286: * @throws ArrayStoreException if the runtime type of a does not allow 287: * an element in this list 288: * @throws NullPointerException if a is null 289: */ 290: public T[] toArray(T[] a) 291: { 292: if (a.length < size) 293: a = (T[]) Array.newInstance(a.getClass().getComponentType(), size); 294: else if (a.length > size) 295: a[size] = null; 296: System.arraycopy(data, 0, a, 0, size); 297: return a; 298: } 299: 300: /** 301: * Retrieves the element at the user-supplied index. 302: * 303: * @param index the index of the element we are fetching 304: * @throws IndexOutOfBoundsException if index < 0 || index >= size() 305: */ 306: public E get(int index) 307: { 308: checkBoundExclusive(index); 309: return data[index]; 310: } 311: 312: /** 313: * Sets the element at the specified index. The new element, e, 314: * can be an object of any type or null. 315: * 316: * @param index the index at which the element is being set 317: * @param e the element to be set 318: * @return the element previously at the specified index 319: * @throws IndexOutOfBoundsException if index < 0 || index >= 0 320: */ 321: public E set(int index, E e) 322: { 323: checkBoundExclusive(index); 324: E result = data[index]; 325: data[index] = e; 326: return result; 327: } 328: 329: /** 330: * Appends the supplied element to the end of this list. 331: * The element, e, can be an object of any type or null. 332: * 333: * @param e the element to be appended to this list 334: * @return true, the add will always succeed 335: */ 336: public boolean add(E e) 337: { 338: modCount++; 339: if (size == data.length) 340: ensureCapacity(size + 1); 341: data[size++] = e; 342: return true; 343: } 344: 345: /** 346: * Adds the supplied element at the specified index, shifting all 347: * elements currently at that index or higher one to the right. 348: * The element, e, can be an object of any type or null. 349: * 350: * @param index the index at which the element is being added 351: * @param e the item being added 352: * @throws IndexOutOfBoundsException if index < 0 || index > size() 353: */ 354: public void add(int index, E e) 355: { 356: checkBoundInclusive(index); 357: modCount++; 358: if (size == data.length) 359: ensureCapacity(size + 1); 360: if (index != size) 361: System.arraycopy(data, index, data, index + 1, size - index); 362: data[index] = e; 363: size++; 364: } 365: 366: /** 367: * Removes the element at the user-supplied index. 368: * 369: * @param index the index of the element to be removed 370: * @return the removed Object 371: * @throws IndexOutOfBoundsException if index < 0 || index >= size() 372: */ 373: public E remove(int index) 374: { 375: checkBoundExclusive(index); 376: E r = data[index]; 377: modCount++; 378: if (index != --size) 379: System.arraycopy(data, index + 1, data, index, size - index); 380: // Aid for garbage collection by releasing this pointer. 381: data[size] = null; 382: return r; 383: } 384: 385: /** 386: * Removes all elements from this List 387: */ 388: public void clear() 389: { 390: if (size > 0) 391: { 392: modCount++; 393: // Allow for garbage collection. 394: Arrays.fill(data, 0, size, null); 395: size = 0; 396: } 397: } 398: 399: /** 400: * Add each element in the supplied Collection to this List. It is undefined 401: * what happens if you modify the list while this is taking place; for 402: * example, if the collection contains this list. c can contain objects 403: * of any type, as well as null values. 404: * 405: * @param c a Collection containing elements to be added to this List 406: * @return true if the list was modified, in other words c is not empty 407: * @throws NullPointerException if c is null 408: */ 409: public boolean addAll(Collection c) 410: { 411: return addAll(size, c); 412: } 413: 414: /** 415: * Add all elements in the supplied collection, inserting them beginning 416: * at the specified index. c can contain objects of any type, as well 417: * as null values. 418: * 419: * @param index the index at which the elements will be inserted 420: * @param c the Collection containing the elements to be inserted 421: * @throws IndexOutOfBoundsException if index < 0 || index > 0 422: * @throws NullPointerException if c is null 423: */ 424: public boolean addAll(int index, Collection c) 425: { 426: checkBoundInclusive(index); 427: Iterator itr = c.iterator(); 428: int csize = c.size(); 429: 430: modCount++; 431: if (csize + size > data.length) 432: ensureCapacity(size + csize); 433: int end = index + csize; 434: if (size > 0 && index != size) 435: System.arraycopy(data, index, data, end, size - index); 436: size += csize; 437: for ( ; index < end; index++) 438: data[index] = itr.next(); 439: return csize > 0; 440: } 441: 442: /** 443: * Removes all elements in the half-open interval [fromIndex, toIndex). 444: * Does nothing when toIndex is equal to fromIndex. 445: * 446: * @param fromIndex the first index which will be removed 447: * @param toIndex one greater than the last index which will be removed 448: * @throws IndexOutOfBoundsException if fromIndex > toIndex 449: */ 450: protected void removeRange(int fromIndex, int toIndex) 451: { 452: int change = toIndex - fromIndex; 453: if (change > 0) 454: { 455: modCount++; 456: System.arraycopy(data, toIndex, data, fromIndex, size - toIndex); 457: size -= change; 458: } 459: else if (change < 0) 460: throw new IndexOutOfBoundsException(); 461: } 462: 463: /** 464: * Checks that the index is in the range of possible elements (inclusive). 465: * 466: * @param index the index to check 467: * @throws IndexOutOfBoundsException if index > size 468: */ 469: private void checkBoundInclusive(int index) 470: { 471: // Implementation note: we do not check for negative ranges here, since 472: // use of a negative index will cause an ArrayIndexOutOfBoundsException, 473: // a subclass of the required exception, with no effort on our part. 474: if (index > size) 475: throw new IndexOutOfBoundsException("Index: " + index + ", Size: " 476: + size); 477: } 478: 479: /** 480: * Checks that the index is in the range of existing elements (exclusive). 481: * 482: * @param index the index to check 483: * @throws IndexOutOfBoundsException if index >= size 484: */ 485: private void checkBoundExclusive(int index) 486: { 487: // Implementation note: we do not check for negative ranges here, since 488: // use of a negative index will cause an ArrayIndexOutOfBoundsException, 489: // a subclass of the required exception, with no effort on our part. 490: if (index >= size) 491: throw new IndexOutOfBoundsException("Index: " + index + ", Size: " 492: + size); 493: } 494: 495: /** 496: * Remove from this list all elements contained in the given collection. 497: * This is not public, due to Sun's API, but this performs in linear 498: * time while the default behavior of AbstractList would be quadratic. 499: * 500: * @param c the collection to filter out 501: * @return true if this list changed 502: * @throws NullPointerException if c is null 503: */ 504: boolean removeAllInternal(Collection c) 505: { 506: int i; 507: int j; 508: for (i = 0; i < size; i++) 509: if (c.contains(data[i])) 510: break; 511: if (i == size) 512: return false; 513: 514: modCount++; 515: for (j = i++; i < size; i++) 516: if (! c.contains(data[i])) 517: data[j++] = data[i]; 518: size -= i - j; 519: return true; 520: } 521: 522: /** 523: * Retain in this vector only the elements contained in the given collection. 524: * This is not public, due to Sun's API, but this performs in linear 525: * time while the default behavior of AbstractList would be quadratic. 526: * 527: * @param c the collection to filter by 528: * @return true if this vector changed 529: * @throws NullPointerException if c is null 530: * @since 1.2 531: */ 532: boolean retainAllInternal(Collection c) 533: { 534: int i; 535: int j; 536: for (i = 0; i < size; i++) 537: if (! c.contains(data[i])) 538: break; 539: if (i == size) 540: return false; 541: 542: modCount++; 543: for (j = i++; i < size; i++) 544: if (c.contains(data[i])) 545: data[j++] = data[i]; 546: size -= i - j; 547: return true; 548: } 549: 550: /** 551: * Serializes this object to the given stream. 552: * 553: * @param s the stream to write to 554: * @throws IOException if the underlying stream fails 555: * @serialData the size field (int), the length of the backing array 556: * (int), followed by its elements (Objects) in proper order. 557: */ 558: private void writeObject(ObjectOutputStream s) throws IOException 559: { 560: // The 'size' field. 561: s.defaultWriteObject(); 562: // We serialize unused list entries to preserve capacity. 563: int len = data.length; 564: s.writeInt(len); 565: // it would be more efficient to just write "size" items, 566: // this need readObject read "size" items too. 567: for (int i = 0; i < size; i++) 568: s.writeObject(data[i]); 569: } 570: 571: /** 572: * Deserializes this object from the given stream. 573: * 574: * @param s the stream to read from 575: * @throws ClassNotFoundException if the underlying stream fails 576: * @throws IOException if the underlying stream fails 577: * @serialData the size field (int), the length of the backing array 578: * (int), followed by its elements (Objects) in proper order. 579: */ 580: private void readObject(ObjectInputStream s) 581: throws IOException, ClassNotFoundException 582: { 583: // the `size' field. 584: s.defaultReadObject(); 585: int capacity = s.readInt(); 586: data = (E[]) new Object[capacity]; 587: for (int i = 0; i < size; i++) 588: data[i] = (E) s.readObject(); 589: } 590: }



 
  
 
  

你可能感兴趣的:(JAVA和J2EE,数据结构)