zoukankan      html  css  js  c++  java
  • 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);
     }

     

  • 相关阅读:
    POJ 1325、ZOJ 1364、HDU 1150 Machine Schedule
    约数的计算
    Opencv距离变换distanceTransform应用——细化字符轮廓&&查找物体质心
    霍夫圆变换
    【奇葩笔试】—— printf() 作为函数的参数及其返回值
    【奇葩笔试】—— printf() 作为函数的参数及其返回值
    字典(dictionary)的设计
    字典(dictionary)的设计
    极值点、驻点、鞍点、拐点
    极值点、驻点、鞍点、拐点
  • 原文地址:https://www.cnblogs.com/cranfield/p/13342044.html
Copyright © 2011-2022 走看看