欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

week06_day06_自己实现ArrayList_2

程序员文章站 2022-05-09 14:08:19
...

关于Iterator中的remove方法:
week06_day06_自己实现ArrayList_2
为什么删除倒数第二个元素不会报并发修改异常?
week06_day06_自己实现ArrayList_2
看一下成果,自己设计的ArrayList:
MyIterator接口:

package com.cskaoyan.list;

/**
 * @author shihao
 * @create 2020-05-16 8:09
 */

import java.util.Iterator;

//MyIterator模仿的是listIterator
//MyIterator
public interface MyIterator<E> extends Iterator<E> {
    boolean hasNext();

    boolean hasPrevious();

    E next();

    E previous();

    int nextIndex();

    int previousIndex();

    void add(E e);

    void remove();

    void set(E e);
}


MyList接口:

package com.cskaoyan.list;

/**
 * @author shihao
 * @create 2020-05-15 17:28
 */

//想让MyArrayList和LinkedList也可以实现自己的foreach循环,
// 就得让MyList实现接口Iterable,表示有迭代的能力
public interface MyList<E> extends Iterable<E> {

    boolean add(E e);

    void add(int index, E e);

    void clear();

    boolean contains(Object obj);

    E get(int index);

    int indexOf(Object obj);

    boolean isEmpty();

    //MyIterator模仿的是listIterator
    //应当实现一个迭代器,对MyList进行遍历
    MyIterator iterator();

    MyIterator iterator(int index);

    int lastIndexOf(Object obj);

    E remove(int index);

    boolean remove(Object obj);

    E set(int index, E e);

    int size();
}

MyArrayList:

package com.cskaoyan.list;

import com.cskaoyan.exception.ArrayOverflowException;

import java.util.*;

/**
 * @author shihao
 * @create 2020-05-15 17:28
 */
//想让MyArrayList也可以实现自己的foreach循环,就得让MyList实现接口Iterable
public class MyArrayList<E> implements MyList<E> {

    //elements默认的大小
    private static final int DEFAULT_CAPACITY = 10;
    //elements的最大容量
    private static final int MAX_CAPACITY = Integer.MAX_VALUE - 8;

    //属性
    //不能直接创建泛型数组,泛型擦除之后,其实数组里面的元素是Object类型
    private Object[] elements;
    private int size;

    //为了防止并发修改异常
    private int modCount;    //统计集合结构被修改的次数


    //构造方法
    public MyArrayList() {
        this.elements = new Object[DEFAULT_CAPACITY];
    }

    public MyArrayList(int initialCapacity) {
        //及早失败原则
        if (initialCapacity <= 0 || initialCapacity > MAX_CAPACITY) {
            throw new IllegalArgumentException("initialCapacity = " + initialCapacity);
        }
        elements = new Object[initialCapacity];
    }


    //方法

    /**
     * 在线性表最后添加元素
     *
     * @param e 待添加的元素
     * @return 如果添加成功返回true
     * 如果添加失败就抛出异常
     */
    @Override
    public boolean add(E e) {
        add(size, e);
        return true;
    }

    /**
     * 在线性表中指定位置添加元素
     *
     * @param index 索引
     * @param e     待添加的元素
     */
    @Override
    public void add(int index, E e) {
        checkIndexForAdd(index);

        //判断是否需要扩容
        if (size == elements.length) {
            int minCapacity = size + 1;
            //通过calculateCapacity的计算,一定可以得到一个新的大于minCapacity的容量
            int newCapacity = calculateCapacity(minCapacity);
            //将容量扩充到newCapacity
            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;
    }

    private int calculateCapacity(int minCapacity) {
        //minCapacity<0表示的情况可能是,两个很大的list进行合并,发生了栈溢出
        if (minCapacity > MAX_CAPACITY || minCapacity < 0) {
            throw new ArrayOverflowException("minCapacity = " + minCapacity);
        }

        //执行到这步说明一定能够容纳minCapacity个元素
        int newCapacity = elements.length + elements.length >> 1;
        if (newCapacity > MAX_CAPACITY || newCapacity < 0) {
            newCapacity = MAX_CAPACITY;
        }

        //返回minCapacity和newCapacity的最大值
        //因为使用addAll方法添加一个集合时,minCapacity可能大于newCapacity
        //最终的返回值应当>=minCapacity
        return Math.max(newCapacity, minCapacity);
    }

    private void checkIndexForAdd(int index) {
        if (index < 0 || index > size) {
            throw new IndexOutOfBoundsException("index=" + index + ", size=" + size);
        }
    }

    /**
     * 清空所有元素
     */
    @Override
    public void clear() {
        for (int i = 0; i < size; i++) {
            elements[i] = null;
        }
        //直接size = 0; 会存在什么问题? 内存泄漏,elements中的元素(引用类型)可能已经
        //变成垃圾了,但是在elements中还存有这些引用,所以GC是不会把他们判断成垃圾的
        size = 0;
        modCount++;
    }

    /**
     * 判断集合中是否存在与集合中指定对象相等的元素
     *
     * @param obj 指定对象
     * @return 如果集合中有与指定对象相等的元素,返回true,否则返回false。
     */
    @Override
    public boolean contains(Object obj) {
        return indexOf(obj) != -1;
    }

    /**
     * 获取指定索引位置的元素
     *
     * @param index 索引
     * @return 指定索引位置的元素
     */
    @Override
    @SuppressWarnings("unchecked")
    public E get(int index) {
        checkIndex(index);
        return (E) elements[index];
    }

    private void checkIndex(int index) {
        if (index < 0 || index >= size) {
            throw new IndexOutOfBoundsException("index = " + index + ", size = " + size);
        }
    }

    /**
     * 获取集合中第一个与指定对象相等元素的索引
     *
     * @param obj 指定对象
     * @return 第一个与指定对象相等元素的索引,如果不存在这样的元素,返回-1
     */
    @Override
    public int indexOf(Object obj) {
        //如果我obj == null,在null上调用equals方法会抛出空指针异常,
        //判断null和elements[i]是否相等得用null == elements[i]
        //所以得判断if (obj == null)
        if (obj == null) {
            for (int i = 0; i < size; i++) {
                if (obj == elements[i]) {
                    return i;
                }
            }
        } else {
            for (int i = 0; i < size; i++) {
                //不用重写equals方法,哪种类对象调用equals方法,就在此类中重写equals方法
                //比如说,elements里面存储的是String类型的对象,就在String中重写equals方法
                //不能写成elements[i].equals(obj),elements[i]可能为null,这样调用会抛出空指针异常
                if (obj.equals(elements[i])) {
                    return i;
                }
            }
        }
        return -1;
    }

    /**
     * 判断集合是否为空
     *
     * @return 如果集合为空返回true,否则返回false
     */
    @Override
    public boolean isEmpty() {
        return size == 0;
    }


    /**
     * 获取集合中最后一个与指定对象相等的元素的索引
     *
     * @param obj 指定对象
     * @return 最后一个与指定对象相等的元素的索引,如果不存在,返回-1
     */
    @Override
    public int lastIndexOf(Object obj) {
        if (obj == null) {
            for (int i = size - 1; i >= 0; i--) {
                if (elements[i] == obj) {
                    return i;
                }
            }
        } else {
            for (int i = size - 1; i >= 0; i--) {
                if (obj.equals(elements[i])) {
                    return i;
                }
            }
        }

        return -1;
    }

    /**
     * 删除指定索引位置的元素
     *
     * @param index 索引
     * @return 被删除的元素
     */
    @Override
    @SuppressWarnings("unchecked")
    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];
        }
        //最后一个元素没有真正的移除掉,可能会导致内存泄漏,应当将其置为null
        elements[size - 1] = null;
        size--;
        modCount++;
        return removeValue;
    }

    /**
     * 删除第一个与指定对象相等的元素
     *
     * @param obj 指定对象
     * @return 如果删除成功返回true,失败返回false
     */
    @Override
    public boolean remove(Object obj) {
        int index = indexOf(obj);
        if (index == -1) {
            return false;
        }
        remove(index);
        return true;
    }

    /**
     * 替换指定索引位置的元素
     *
     * @param index 索引
     * @param e     新值
     * @return 该索引位置原来的值
     */
    @Override
    @SuppressWarnings("unchecked")
    public E set(int index, E e) {
        checkIndex(index);
        E oldValue = (E) elements[index];
        elements[index] = e;
        //只是替换元素的值,并没有修改集合的结构,不需要modCount++
        return oldValue;
    }

    /**
     * 获取线性表中的元素个数
     *
     * @return 元素个数
     */
    @Override
    public int size() {
        return size;
    }

    /**
     * 获取迭代器,默认下一个元素的索引值为0
     *
     * @return 迭代器
     */
    //迭代器是以成员位置内部类的形式存在的
    @Override
    public MyIterator<E> iterator() {
        //只需创建一个迭代器,将他返回就可以了
        return new Itr();
    }

    /**
     * 获取迭代器,指定下一个元素的索引值为index
     *
     * @param index 下一个元素的索引值
     * @return 迭代器
     */
    @Override
    public MyIterator<E> iterator(int index) {
        checkIndexForAdd(index);
        return new Itr(index);
    }

    //迭代器的重点就是创建成员位置内部类Itr
    private class Itr implements MyIterator<E> {
        //属性
        int cursor;
        //可以在最后通过判断excModCount == modCount来判断是否发生并发修改异常
        //为什么可以访问modCount? 因为内部类可以访问外部类的成员
        int excModCount = modCount;  //期望被修改的次数
        //仅仅知道光标位置,不知道是正向遍历还是逆向遍历,无法返回最近遍历到的元素的索引
        int lastRet = -1;  //最近返回元素的索引
        //-1表示最近没有返回元素,或者是最近返回的元素是失效的
        //构造方法


        public Itr() {
        }

        public Itr(int index) {
            this.cursor = index;
        }

        //方法

        /**
         * 判断光标后面是否还有元素
         *
         * @return 如果光标后面还有元素返回true,否则返回false
         */
        @Override
        public boolean hasNext() {
            return cursor != size;
        }

        /**
         * 判断光标前面是否还有元素
         *
         * @return 如果光标前面还有元素返回true,否则返回false
         */
        @Override
        public boolean hasPrevious() {
            return cursor != 0;
        }

        /**
         * 将光标往后移动一个位置,并把被光标越过的元素返回
         *
         * @return 被光标越过的元素
         */
        @Override
        @SuppressWarnings("unchecked")
        public E next() {
            //首先要检查迭代器是否还有效,是否发生了并发修改异常
            checkConModException();
            //后面没有元素了你还想往后移就抛出异常
            if (!hasNext()) {
                throw new NoSuchElementException();
            }

            //移动光标
/*            E retValue = (E) elements[cursor];
            lastRet = cursor;
            cursor++;
            return retValue;*/

            lastRet = cursor;
            return (E) elements[cursor++];
        }

        private void checkConModException() {
            if (excModCount != modCount) {
                throw new ConcurrentModificationException();
            }
        }

        /**
         * 将光标往前移动一个位置,并把被光标越过的元素返回
         *
         * @return 被光标越过的元素
         */
        @Override
        @SuppressWarnings("unchecked")
        public E previous() {
            //判断迭代器是否有效
            checkConModException();
            if (!hasPrevious()) {
                throw new NoSuchElementException();
            }
            //移动光标
/*            E retValue = (E) elements[cursor - 1];
            lastRet = cursor - 1;
            cursor--;
            return retValue;*/

            lastRet = --cursor;
            return (E) elements[cursor];
        }

        /**
         * 获取光标后面元素的索引
         *
         * @return 光标后面元素的索引
         */
        @Override
        public int nextIndex() {
            return cursor;
        }

        /**
         * 获取光标前面元素的索引
         *
         * @return 光标前面元素的索引
         */
        @Override
        public int previousIndex() {
            return cursor - 1;
        }


        /**
         * 在光标后面添加元素
         *
         * @param e 待添加的元素
         */
        @Override
        public void add(E e) {
            //判断迭代器是否有效
            checkConModException();
            MyArrayList.this.add(cursor, e);
            excModCount = modCount;
            //最近返回的元素是失效的
            lastRet = -1;
            cursor++;
        }

        @Override
        public void remove() {
            checkConModException();
            //没有最近返回的元素或者最近返回的元素失效就抛出非法状态异常
            if (lastRet == -1) {
                throw new IllegalStateException();
            }

            //删除最近返回的元素
            MyArrayList.this.remove(lastRet);
            //更改迭代器的属性
            excModCount = modCount;
            cursor = lastRet;
            lastRet = -1;
        }

        /**
         * 用新值e替换最近返回的元素
         *
         * @param e 新值
         */
        @Override
        public void set(E e) {
            checkConModException();
            if (lastRet == -1) {
                throw new IllegalStateException();
            }

            elements[lastRet] = e;
            //索引为lastRet的elements的值已经修改了,lastRet指向的已经不是原来的值了,
            //而是指向修改后的值。也就是说最近返回的元素应当失效,将lastRet置为-1
            lastRet=-1;
            //但是cursor是不需要改变的
            //而且modCount也不需要改变,因为没有修改集合的结构。
        }
    }

    @Override
    public String toString() {
        //[a, b, c]
        StringBuilder sb = new StringBuilder("[");
        for (int i = 0; i < size; i++) {
            //if (i != 0) sb.append(", ");
            //if (i != size - 1) sb.append(", ")
            //以上两者都不用,进行判断和-1操作会使性能降低
            sb.append(elements[i]).append(", ");
        }

        //size = 0 说明就没有进行上述循环,不需要删除sb末尾的", "
        if (size != 0) {
            sb.delete(sb.length() - 2, sb.length());
        }

        return sb.append("]").toString();
    }

    //测试代码
    public static void main(String[] args) {

        //测试toString方法、add方法、size方法
        MyList<String> list = new MyArrayList<>();
//        System.out.println(list);
        list.add("hello");
        list.add("world");
        list.add("java");
//        System.out.println(list);
//        System.out.println(list.size());

        //测试自动扩容
//        for (int i = 0; i < 30; i++) {
//            list.add(" i");
//        }
//        System.out.println(list.size());

        //测试void add(int index, E e)
//        list.add(0,null);
//        list.add(2,null);
//        list.add(-1,"hao");
//        System.out.println(list);

        //测试boolean isEmpty()、void clear()
//        System.out.println(list.size());
//        System.out.println(list.isEmpty());
//        list.clear();
//        System.out.println(list.isEmpty());
//        System.out.println(list);
//        System.out.println(list.size());

        // 测试boolean contains(Object obj)
//        System.out.println(list.contains("hello"));
//        System.out.println(list.contains("helloKitty"));
//        System.out.println(list.contains(null));
//        list.add(null);
//        System.out.println(list.contains(null));

        // 测试int indexOf(Object obj)
//        list.add("hello");
//        System.out.println(list.indexOf("hello")); // 0
//        System.out.println(list.indexOf(null));  // -1
//        list.add(null);
//        list.add(1, null);
//        System.out.println(list.indexOf(null)); // 1

        // 测试int lastIndexOf(Object obj)
        /*list.add("hello");
        System.out.println(list.lastIndexOf("hello")); // 3
        System.out.println(list.lastIndexOf(null)); // -1
        list.add(null);
        list.add(1, null);
        System.out.println(list.lastIndexOf(null)); // 5*/

        // 测试E get(int index)
//        String s = list.get(1);
//        System.out.println(s);
        // System.out.println(list.get(-1));
        // System.out.println(list.get(list.size()));


        //测试E remove(int index)
        //System.out.println(list.remove(1));
        //System.out.println(list.remove(-1));
        //System.out.println(list.remove(list.size()));
        //System.out.println(list);

        // 测试boolean remove(Object obj)
        // list.add("hello");
        // System.out.println(list.remove("hello"));
        // System.out.println(list.remove(null));
        /*list.add(null);
        list.add(0, null);
        System.out.println(list);
        System.out.println(list.remove(null));
        System.out.println(list);*/


        // 测试E set(int index, E e)
        // System.out.println(list.set(1, "WORLD"));
        // System.out.println(list.set(-1, "WORLD"));
        // System.out.println(list.set(list.size(), "WORLD"));
        // System.out.println(list);

        //*****************************************************************//
        //------------------------Iterator---------------------------------//
        //*****************************************************************//


        // 测试boolean hasNext(), boolean hasPrevious()
        // MyIterator<String> it = list.iterator();
        // MyIterator<String> it = list.iterator(list.size());
        // MyIterator<String> it = list.iterator(1);
        // System.out.println(it.hasPrevious());
        // System.out.println(it.hasNext());

        // 测试E next()
        /*MyIterator<String> it = list.iterator();
        System.out.println(it.next());
        System.out.println(it.next());
        System.out.println(it.next());*/
        // System.out.println(it.next()); NoSuchElementException

        // 测试E previous()
        /*MyIterator<String> it = list.iterator(list.size());
        System.out.println(it.previous());
        System.out.println(it.previous());
        System.out.println(it.previous());*/
        // System.out.println(it.previous()); NoSuchElementException

        // 测试int nextIndex(), int previousIndex()
        /*MyIterator<String> it = list.iterator();
        System.out.println(it.previousIndex()); //-1
        System.out.println(it.nextIndex()); // 0
        it.next();
        System.out.println(it.previousIndex()); // 0
        System.out.println(it.nextIndex());*/ // 1

        /*MyIterator<String> it = list.iterator(list.size());
        System.out.println(it.previousIndex()); // 2
        System.out.println(it.nextIndex()); // 3
        it.previous();
        System.out.println(it.previousIndex()); //1
        System.out.println(it.nextIndex()); // 2*/

        //测试 void add(E e)
        /*MyIterator<String> it = list.iterator();
        it.next();
        it.add("wuhan");
        it.add("beijing");
        System.out.println(list);*/

        //测试 void add(E e)的并发修改异常
        /*for(MyIterator<String> it = list.iterator(); it.hasNext(); ) {
            String s = it.next();
            if ("hello".equals(s)) {
                int index = it.nextIndex();
                list.add(index, "wuhan");
            }
        }
        System.out.println(list);*/

        // 测试void remove()
        /*MyIterator<String> it = list.iterator();
        // it.remove(); // IllegalStateException
        it.next();
        it.remove();
        System.out.println(list);
        // it.remove(); IllegalStateException*/

        /*MyIterator<String> it = list.iterator(list.size());
        // it.remove();
        it.previous();
        it.remove();
        System.out.println(list);
        // it.remove();*/

        // 测试ConcurrentModificationException
        /*for(MyIterator<String> it = list.iterator(); it.hasNext(); ) {
            String s = it.next();
            if ("hello".equals(s)) {
                int index = it.previousIndex();
                list.remove(index);
            }
        }
        System.out.println(list);*/


        // void set(E e)
/*        MyIterator<String> it = list.iterator();
        // it.set("HELLO");
        it.next();
        it.set("HELLO");
        System.out.println(list);
        // it.set("hello");
        //调用set后cursor并没有前移或后移
        System.out.println(it.nextIndex());*/

        //set方法并没有修改集合的结构,所以不会报并发修改异常
/*        for(MyIterator<String> it = list.iterator(); it.hasNext(); ) {
            String s = it.next();
            if ("hello".equals(s)) {
                int index = it.previousIndex();
                list.set(index, "HELLO");
            }
        }
        System.out.println(list);*/

        //会发现我们删除"world"和"java"时不会报并发修改异常的错
        //即:在MyIterator中调用MyArrayList的remove方法删除倒数第二个元素和倒数第一个元素时,
        // 不会报并发修改异常
        /*for(MyIterator<String> it = list.iterator(); it.hasNext(); ) {
            String s = it.next();
            if ("java".equals(s)) {
                int index = it.previousIndex();
                list.remove(index);
            }
        }
        System.out.println(list);*/

//        ArrayList<String> list = new ArrayList<>();
//        list.add("hello");
//        list.add("world");
//        list.add("java");
//
          //不光我们自己写的Iterator会出现这种问题,jdk中的Iterator也会出现类似问题
          //即:在迭代器中调用ArrayList的remove方法删除倒数第二个元素时,不会报并发修改异常
//        for(ListIterator<String> it = list.listIterator(); it.hasNext(); ) {
//            String s = it.next();
//            if ("java".equals(s)) {
//                int index = it.previousIndex();
//                list.remove(index);
//            }
//        }
//        System.out.println(list);

    }

}