MyArrayList的实现
定义两个接口MyList和MyIterator。定义好集合和迭代器的规则(方法只实现增删改查及迭代器)
public interface MyList<E> extends Iterable<E> { boolean add(E e); void add(int index, E e); E remove(int index); boolean remove(Object obj); void clear(); boolean contains(Object obj); E get(int index); int index(Object obj); int lastIndexOf(Object obj); MyIterator<E> iterator(); MyIterator<E> iterator(int index); }
import java.util.Iterator; public interface MyIterator<E> extends Iterator<E> { boolean hasNext(); boolean hasPrevious(); E next(); E previous(); int nextIndex(); int prevIndex(); void add(E e); void remove(); void set(E e); }
对于ArrayList,要对其常量、属性、构造方法、API进行设计
常量:要确定底层数组的最大容量和默认容量
private static final int DEFAULT_CAPACITY = 10; private static final int MAX_CAPACITY = Ineger.MAX_VALUE - 8;
属性:底层是数组
private Object[] elements; private int size; private int modCount; //统计集合结构修改的次数,验证迭代器是否失效
构造方法
public MyArrayList() { elements = new Object[DEFAULT_CAPACITY]; } public MyArrayList(int initialCapacity) { if (initialCapacity <= 0 || initialCapacity > MAX_CAPACITY) throw new IllegalArgumentException("initialCapacity=" + initialCapacity); elements = new Object[initialCapacity]; }
API:
增: boolean add(E e) void add(int index, E e)
tips:
-
计算扩容长度时,可以使用移位运算符
-
在grow()扩容方法中,要记得将elements数组指向已经扩容好的数组
public boolean add(E e) { add(size, e); return true; }
public void add(int index, E e) { //判断索引是否合法 checkIndexForAdd(index); //判断是否需要扩容 if (size == elements.length) { // 计算新数组的容量 int minCapacity = size + 1; int newCapacity = calculateCapacity(minCapacity); // 扩容 grow(newCapacity); } // 添加元素 for (int i = size; i > index; i--) { elements[i] = elements[i - 1]; } elements[index] = e; size++; modCount++; } private void grow(int newCapacity) { Object[] newArr = new Object[newCapacity]; // 复制数据 for (int i = 0; i < size; i++) { newArr[i] = elements[i]; } // elements指向新数组 elements = newArr; // Caution! } private int calculateCapacity(int minCapacity) { if (minCapacity > MAX_CAPACITY || minCapacity < 0) { throw new ArrayOverflowException(); } // 一定容纳下minCapacity个元素 int newLength = elements.length + (elements.length >> 1); if (newLength > MAX_CAPACITY || newLength < 0) { newLength = MAX_CAPACITY; } // 返回minCapacity和newLength的最大值 return newLength > minCapacity ? newLength : minCapacity; } private void checkIndexForAdd(int index) { if (index < 0 || index > size) { throw new IndexOutOfBoundsException("index=" + index + ", size=" + size); } }
删:E remove (int index) boolean remove(Object obj) void clear()
tips:
-
删除操作所检查的索引范围时[0,size-1],注意与增加操作区分开
-
clear()中,要把所有元素值置为null,避免内存泄漏
public E remove(int index) { //首先要检查索引的合法性 checkIndex(index); E removeValue = (E) elements[index]; //将索引后的元素都前移 for (int i = index; i < size - 1; i++) { elements[i] = elements[i + 1]; } elements[--size] = null; //最后一个元素置为null值,避免内存泄漏 modCount++; return removeValue; } private void checkIndex(int index) { //,注意检查的值与检查增加的索引值不同。范围[0,size-1] if (index < 0 || index >= size) throw new IndexOutOfBoundsException("index=" + index + ",size=" + size); }
public boolean remove(Object obj) { //这里调用indexOf(obj)就可以找到目标索引。有了索引直接调用remove(index)即可 int index = indexOf(obj); if (index == -1) return false; remove(index); return true; }
public void clear() { //把集合中的元素都置为null值 for (int i = 0; i < size; i++) { elements[i] = null; } size = 0; modCount++; //修改了集合结构 }
改: E set(int index, E e)
public E set(int index, E e) { checkIndex(index); E oldValue = (E) elements[index]; elements[index] = e; return oldValue; }
查: int indexOf(Object obj) lastIndexOf(Object obj) boolean contains(Object obj)
E get(int index) boolean isEmpty() int size()
tips:
-
注意要判断指定对象的值是否为null(分情况),因为List集合是可以存储null元素的。判断的时候也要分情况,看代码吧
public int indexOf(Object obj) { if (obj == null) { for (int i = 0; i < size; i++) { if (elements[i] == null) return i; } } else { for (int i = 0; i < size; i++) { if (obj.equals(elements[i])) return i; } } return -1; }
public int lastIndexOf(Object obj) { if (obj == null) { for (int i = size - 1; i >= 0; i--) { if (obj.equals(elements[i])) return i; } } else { for (int i = size - 1; i >= 0; i--) { if (obj.equals(elements[i])) return i; } } return -1; }
public boolean contains(Object obj) { return indexOf(obj) != -1; }
public E get(int index) { checkIndex(index); return (E) elements[index]; }
public boolean isEmpty() { return size == 0; }
public int size() { return size; }
迭代器部分
迭代器使用内部类方式定义
属性
int cursor; int lastRet = -1; //最近返回元素的索引,-1代表最近未返回元素 int expCount = modCount; //当前集合修改次数为期望次数
构造方法
Itr() {} Itr(int index) { cursor = index; //将光标置为index }
API
public boolean hasNext() { return cursor != size; }
public boolean hasPrevious() { return cursor != 0; }
public E next() { //判断迭代器是否有效 checkConModException(); //判断光标后面还有元素嘛 if (!hasNext()) throw new NoSuchElementException(); //简化了步骤,返回的是光标经过的值,也就是当前光标后的索引 //lastRet = cursor,然后cursor后移 lastRet = cursor++; return (E) elements[lastRet]; } private void checkConModException() { if (expCount != modCount) throw new ConcurrentModificationException(); }
public E previous() { checkConModException(); if (!hasPrevious()) throw new NoSuchElementException(); //cursor指向的元素是前面那一个,lastRet要先--在获取 lastRet = --cursor; return (E) elements[lastRet]; }
public int nextIndex() { return cursor; }
public int prevIndex() { return cursor - 1; }
public void add(E e) { checkConModException(); //这里是内部类,调用外部类方法,必须要借助外部类对象 //这里和remove不同,lastRet可能为-1.cursor为0 MyArrayList.this.add(cursor, e); //这里modCount++,修改了集合结构 //修改迭代器属性 expCount = modCount; //通过迭代器修改,允许修改,将expCount的值变为当前修改次数 lastRet = -1; //修改之后,上次返回值索引变为-1 cursor++; }
public void remove() { checkConModException(); //判断最近是否有返回元素,因为删除操作必须对最近返回的元素(lastRet)进行删除,光标只负责指向的 if (lastRet == -1) throw new IllegalArgumentException(); MyArrayList.this.remove(lastRet); expCount = modCount; //关于cursor的变化,这里要分正向和逆向遍历讨论。结合画图可以找到对应规律!! cursor = lastRet; //!!!!! lastRet = -1; }
注意删除操作中,要判断当前是正向遍历还是逆向遍历。画出图可得知相应规律(删除后的cursor始终等于lastRet)
public void set(E e) { checkConModException(); if (lastRet == -1) throw new IllegalArgumentException(); elements[lastRet] = e; lastRet = -1; }
外部类调用了Iterator内部类的两个构造方法对创建Itr对象
public MyIterator<E> iterator() { return new Itr(); }
public MyIterator<E> iterator(int index) { checkIndex(index); return new Itr(index); }