MyArrayList的实现

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);
 }

 

原文地址:https://www.cnblogs.com/cranfield/p/13342044.html