ArrayList 类提供了 List ADT 的可增长数组的实现。
MyArrayList 泛型类实现了 Iterable 接口从而可以拥有增强 for 循环(for each 循环)。
public class MyArrayList<AnyType> implements Iterable<AnyType> { @Override public Iterator<AnyType> iterator() { return new ArrayListIterator(); } }
MyArrayList 是基于数组实现的,其属性有:
private static final int DEFAULT_CAPACITY = 10; private int theSize; private AnyType[] theArrays;
其中常量 DEFAULT_CAPACITY 表示数组的基础容量。theSize 表示数组表当前长度(数组元素个数),作索引时,A[theSize - 1] 表示数组的最后一个元素,而 A[theSize] 表示新添加的项可以被放置的位置。泛型数组 theArrays 为 MyArrayList 类的数组实现,即对 MyArrayList 对象的操作实际为对数组 theArrays 的操作。
当实例化 MyArrayList 对象时,调用构造方法:
public MyArrayList(){ theArrays = (AnyType[])new Object[DEFAULT_CAPACITY]; doClear(); }
在构造方法中先实例化泛型数组 theArrays。由于泛型数组的创建是非法的,所以我们需要创建一个泛型类型限界的数组;即创建一个 Object[] 类型的数组,然后向泛型类型数组 AnyType[] 强制转型。
然后调用 doClear() 方法对数组表进行清空、初始化的操作,此方法仅类内部可调用:
private void doClear(){ theSize = 0; expandCapacity(DEFAULT_CAPACITY); }
此处先设置 theSize = 0,然后调用 expandCapacity() 方法改变数组容量为基础容量。(在 expandCapacity() 方法的实现中,若扩充的容量(参数)小于 theSize 时表示非法的操作。)
数组扩容方法 expandCapacity() :
public void expandCapacity(int newCapacity){ if (newCapacity < theSize){ return; } // 数组容量的扩充: AnyType[] newArrays = (AnyType[])new Object[newCapacity]; // 利用 System.arraycopy() 方法拷贝数组 System.arraycopy(theArrays,0,newArrays,0,theSize); theArrays = newArrays; }
System.arraycopy() 方法:
public static native void arraycopy(Object src, int srcPos, Object dest, int destPos, int length);
/** * 根据下标得到数组元素的值 */ public AnyType get(int idx){ if (idx <0 || idx >= theSize){ throw new ArrayIndexOutOfBoundsException(); } return theArrays[idx]; }
/** * 根据下标设置数组元素的值 * 返回该下标元素的原值 */ public AnyType set(int idx,AnyType newVal){ if (idx <0 || idx >= theSize){ throw new ArrayIndexOutOfBoundsException(); } AnyType oldVal = theArrays[idx]; theArrays[idx] = newVal; return oldVal; }
/** * 根据下标向数组插入新元素 */ public boolean add(int idx,AnyType newVal){ // 当 idx=theSize 时,在 A[theSize] 位置处插入元素 if (idx < 0 || idx > theSize){ throw new ArrayIndexOutOfBoundsException(); } // 数组满时扩充容量 if (theArrays.length == theSize){ expandCapacity(theSize*2 + 1); } // 数组元素后移 for (int i=theSize;i>idx;i--){ theArrays[i] = theArrays[i-1]; } theArrays[idx] = newVal; theSize++; return true; } /** * 在数组末尾插入新元素 */ public boolean add(AnyType newVal){ add(theSize,newVal); return true; }
/** * 根据下标删除元素 * 返回被删除的元素 */ public AnyType remove(int idx){ if (idx < 0 || idx >= theSize){ throw new ArrayIndexOutOfBoundsException(); } // 数组元素前移 AnyType removedElem = theArrays[idx]; for (int i = idx; i < theSize-1; i++) { theArrays[i] = theArrays[i+1]; } theSize--; return removedElem; }
实现 Iterable 接口的集合必须提供 iterator 方法,该方法返回一个 Iterator (java.util.Iterator)类型的对象:
public interface Iterator<AnyType> { boolean hasNext(); AnyType next(); void remove(); }
即每个集合均可通过 iterator 方法创建并返回给客户一个实现 Iterator 接口的对象,并把 当前位置 的概念在对象内部存储下来。根据当前位置项每次调用 hasNext() 来判断是否存在下一项,调用 next() 来给出下一项,而 remove() 方法则删除由 next() 方法最新返回的项(即当调用一次 remove() 后,直到对 next() 再调用一次后才能调用 remove() 方法)。例:
public static <AnyType> void print(Collection<AnyType> coll){ Iterator<AnyType> itr = coll.iterator(); while(itr.hasNext()){ AnyType item = itr.next(); System.out.println(item); // itr.remove(); } }
Java 中的增强 for 循环(for each)底层即是通过这种迭代器模式来实现的,当使用增强 for 循环时也就是间接的使用 Iterator。
import java.util.Iterator; import java.util.NoSuchElementException; public class MyArrayList<AnyType> implements Iterable<AnyType> { ...... @Override public Iterator<AnyType> iterator() { return new ArrayListIterator(); } private class ArrayListIterator implements Iterator<AnyType>{ // 初始时当前位置为 0 private int current = 0; @Override public boolean hasNext() { return current < theSize; } @Override public AnyType next() { if (!hasNext()){ throw new NoSuchElementException(); } return theArrays[current++]; } @Override public void remove() { /* 因为 remove() 方法有相同的,MyArrayList.this 表示外部类当前对象的一个引用 */ MyArrayList.this.remove(--current); } } }
iterator() 方法直接返回 ArrayListIterator 类的一个实例,该类是一个实现 Iterator 接口的类。ArrayListIterator 存储当前位置的概念,并提供 hasNext()、next()、remove() 的实现。 当前位置 表示要被查看的下一元素(的数组下标),因此初始化当前位置为 0。
其中,泛型类 ArrayListIterator 是一个 内部类 ,使用内部类的目的及优点:
因为 ArrayList 是基于数组实现的,所以和数组相似:ArrayList 的 get() 和 set() 方法花费常数时间 O(1);而 add() 和 remove() 方法需要挨个移动其余数组元素,所以其花费的时间代价为 O(n)。