• Java源码初学_AbstractList&AbstractCollection


    一.AbstractCollection抽象类:(提供了Collection接口的骨干实现,以减少实现接口所需要的工作)

    1.contains方法

      contains方法,通过迭代器对于列表的每一个元素进行遍历,并且判断是否与给定的元素相等.另外由于传入的元素可能为null,因此在执行传入的元素的equals方法的时候,需要先判断是否为null.源代码如下:

    public boolean contains(Object o) {
            Iterator<E> it = iterator();
            if (o==null) {
                while (it.hasNext())   //迭代器的hasNext的实现通常是拿索引和容器容量作比较.因此当容器为null时,返回的是false
                    if (it.next()==null) //只有当迭代的元素为null时返回true,
                        return true;
            } else {
                while (it.hasNext())    //进行迭代.
                    if (o.equals(it.next())) //调用equals方法判断(传入的对象的equals方法)
                        return true;
            }
            return false;//集合为null或者没有找到对应的元素的时候
        }

    2.toArray()方法

      空参的toArray方法,在方法内部定义了一个Object数组,并且对于容器内部的元素采用迭代器进行遍历,并且将每个元素放置于Object数组中.

     public Object[] toArray() {
            // 估计数组的大小;可能会有更多或者更少的元素
            Object[] r = new Object[size()];
            Iterator<E> it = iterator();
            for (int i = 0; i < r.length; i++) {
                if (! it.hasNext()) // 比预想的元素要少
                    return Arrays.copyOf(r, i);
                r[i] = it.next();
            }
            return it.hasNext() ? finishToArray(r, it)/*比预想的元素多*/ : r;
        }

    3.toArray(T[])方法

      这个方法如果给定的数组够大,则将结束索引的元素设为null,否则建立一个新的数组并且返回.

    public <T> T[] toArray(T[] a) {
            /* 将数组的大小设为容器的大小
                参数指定的数组大于容器的大小,则在参数的数组设置值
                否则new一个新的数组
                getComponentType返回的是数组组件的类型
            */int size = size();
            T[] r = a.length >= size ? a :
                      (T[])java.lang.reflect.Array
                      .newInstance(a.getClass().getComponentType(), size);
            Iterator<E> it = iterator();
    
            for (int i = 0; i < r.length; i++) {
                if (! it.hasNext()) { //比预期的更少
                    if (a == r) {
                        r[i] = null; // 将索引i处的位置设为null
                    } else if (a.length < i) {
                        return Arrays.copyOf(r, i);//返回copy的数组(重新新建立一个数组)
                    } else {
                        System.arraycopy(r, 0, a, 0, i);
                        if (a.length > i) {
                            a[i] = null;//将索引i处的位置设为null
                        }
                    }
                    return a;
                }
                r[i] = (T)it.next();
            }
            //比预期的元素更多
            return it.hasNext() ? finishToArray(r, it) : r;
        }
    
    
    private static <T> T[] finishToArray(T[] r, Iterator<?> it) {
            int i = r.length;
            while (it.hasNext()) {
                int cap = r.length;
                if (i == cap) {
                    int newCap = cap + (cap >> 1) + 1;
                    if (newCap - MAX_ARRAY_SIZE > 0)
                        newCap = hugeCapacity(cap + 1);
                    r = Arrays.copyOf(r, newCap);//扩充容量
                }
                r[i++] = (T)it.next();
            }
            // 如果i与r.length相等,返回r否则返回copy的数组
            return (i == r.length) ? r : Arrays.copyOf(r, i);
        }

    4.addAll方法

      addAll方法:直接对Collection中的每个元素进行add方法的调用,只要调用成功了则返回true.

    public boolean addAll(Collection<? extends E> c) {
            boolean modified = false;
            for (E e : c)
                if (add(e))
                    modified = true;//只要添加成功,modified设为true
            return modified;
        }

    二.AbstractList类

     此类提供 List 接口的骨干实现,以最大限度地减少实现“随机访问”数据存储(如数组)支持的该接口所需的工作。对于连续的访问数据(如链表),应优先使用 AbstractSequentialList,而不是此类.   

         要实现可修改的列表,编程人员必须另外重写 set(int, E) 方法(否则将抛出UnsupportedOperationException)。如果列表为可变大小,则编程人员必须另外重写 add(int, E) 和 remove(int) 方法。

        例如在AbstractList中,set方法是这样实现的:

     public E set(int index, E element) {
            throw new UnsupportedOperationException();
        }

        1.indexOf方法

        和contains方法实现几乎相同,但是采用了ListIterator对容器内部的元素进行了遍历:

    public int indexOf(Object o) {
            ListIterator<E> it = listIterator();
            if (o==null) {
                while (it.hasNext())
                    if (it.next()==null)
                        return it.previousIndex();
            } else {
                while (it.hasNext())
                    if (o.equals(it.next()))
                        return it.previousIndex();//此时索引已经加1.需要用privioutsIndex来获取索引
            }
            return -1;
        }

        2.clear方法

     clear方法采用ListIterator来实现对于指针经过的元素进行删除:

     public void clear() {
                removeRange(0, size());//改变容量大小的操作
            }
            protected void removeRange(int fromIndex, int toIndex) {
                ListIterator<E> it = listIterator(fromIndex);
                for (int i=0, n=toIndex-fromIndex; i<n; i++) {
                    it.next();
                    it.remove();
                }
            }

        3.迭代器内部实现:

        在AbstractList中定义了内部类,实现了迭代器接口,实现了对于元素的遍历.Itr(普通迭代器)的实现,在迭代器内部定义了一个指针,每次返回元素后,指针将会指向下一个元素,继续返回,直到计数器达到容器的大小.同时迭代器的实现也解释了不能还没有调用next,就remove,也不能连续调用两次remove方法的原因:未调用next就remove,由于lastret值为-1,会报异常,而在第一次调用remove后,lastret值变为-1,如果再次调用remove也会发生异常!

     private class Itr implements Iterator<E> {
            /**
             * 下一个元素的索引
             */
            int cursor = 0;
    
            /**
             * 前一个元素的索引
             */
            int lastRet = -1;
    
            /**
             * 记录容器被修改的次数
             */
            int expectedModCount = modCount;
    
            public boolean hasNext() {
                return cursor != size();
            }
    
            public E next() {
                checkForComodification();//检查遍历的时候容器有没有被修改过
                try {
                    int i = cursor;
                    E next = get(i);//获取元素
                    lastRet = i; //lastRet记录获取到的元素的索引
                    cursor = i + 1;//cursor+1,准备获取接下来的元素
                    return next;
                } catch (IndexOutOfBoundsException e) {
                    checkForComodification();
                    throw new NoSuchElementException();
                }
            }
    
            public void remove() {
                if (lastRet < 0) //这行代码解释了,不能未调用next()就remove() 也不能连续调用两次remove()
                    throw new IllegalStateException();
                checkForComodification();
    
                try {
                    AbstractList.this.remove(lastRet);
                    if (lastRet < cursor)
                        cursor--;
                    lastRet = -1; 
                    expectedModCount = modCount;
                } catch (IndexOutOfBoundsException e) {
                    throw new ConcurrentModificationException();
                }
            }
    
            final void checkForComodification() {
                if (modCount != expectedModCount)
                    throw new ConcurrentModificationException();
            }
        }

      ListIterator的实现,和Iterator实现基本相同,它继承了Itr类,区别在于它支持在调用next或者privious后,添加元素.这里根据源代码分析.在调用next的时候,调用add,add方法会在cursor指针(下一个要next的元素)的位置添加元素.并将cursor+1,使得next的调用无法返回添加的元素.在调用privious的时候,调用add,add方法会在已经返回的元素的位置处,添加元素,并将cursor+1,这时候,下次返回的将是cursor-1的元素,即新添加的元素.

        private class ListItr extends Itr implements ListIterator<E> {
            ListItr(int index) {
                cursor = index;   //将元素指针设为指定值
            }
    
            public boolean hasPrevious() {
                return cursor != 0;
            }
    
            public E previous() {
                checkForComodification();  //检查是否被修改
                try {
                    int i = cursor - 1;
                    E previous = get(i);
                    lastRet = cursor = i;//结束该方法调用的时候,cursor位置停留在返回的元素的位置上,这点与next不同
                    return previous;
                } catch (IndexOutOfBoundsException e) {
                    checkForComodification();
                    throw new NoSuchElementException();
                }
            }
    
            public int nextIndex() {
                return cursor;
            }
    
            public int previousIndex() {
                return cursor-1;
            }
    
            public void set(E e) {
                if (lastRet < 0)
                    throw new IllegalStateException();
                checkForComodification();
    
                try {
                    AbstractList.this.set(lastRet, e);
                    expectedModCount = modCount;
                } catch (IndexOutOfBoundsException ex) {
                    throw new ConcurrentModificationException();
                }
            }
    
            public void add(E e) {
                checkForComodification();
                /*
                    add方法在next后调用下一个next无法返回新添加的元素
                    在privious后调用,下一个privious可以返回.
                    实际上无论是在next还是在privious方法后调用,
                    add方法都使得指针向前移动了1位.                
                */
                try {
                    int i = cursor;
                    AbstractList.this.add(i, e);
                    lastRet = -1;
                    cursor = i + 1;
                    expectedModCount = modCount;
                } catch (IndexOutOfBoundsException ex) {
                    throw new ConcurrentModificationException();
                }
            }
        }

      4.equals方法的实现:

      equals方法采用ListIterator方法对于每一个元素都进行了遍历,在调用ListIerator方法之前,需要先判断是否指向同一对象或者是否运行时类相同.

             public boolean equals(Object o) {
                    if (o == this)     //是否指向同一个对象
                        return true;
                    if (!(o instanceof List)) //是否都是List对象
                        return false;
            
                    ListIterator<E> e1 = listIterator();
                    ListIterator<?> e2 = ((List<?>) o).listIterator();
                    while (e1.hasNext() && e2.hasNext()) {
                        E o1 = e1.next();
                        Object o2 = e2.next();
                        if (!(o1==null ? o2==null : o1.equals(o2)))
                            return false;
                    }
                    return !(e1.hasNext() || e2.hasNext()); //如果有一方还有多余的元素,那么判断不成立
                }

      5.subList方法

      该方法new了一个List并且在这里List里持有源容器对象的引用.可以通过更改这个List,更改原来的容器(这样的List又称为源容器的视图),offset索引指示了视图与容器的偏移量.

    /*
        该方法new了一个List并且在这里List里持有源容器对象的引用.
        可以通过更改这个List,更改原来的容器
    */
    public List<E> subList(int fromIndex, int toIndex) {
            return (this instanceof RandomAccess ?
                    new RandomAccessSubList<>(this, fromIndex, toIndex) :
                    new SubList<>(this, fromIndex, toIndex));
        }
     class SubList<E> extends AbstractList<E> {
        private final AbstractList<E> l;//持有原容器对象的引用
        private final int offset;
        private int size;
    
        SubList(AbstractList<E> list, int fromIndex, int toIndex) {
            if (fromIndex < 0)
                throw new IndexOutOfBoundsException("fromIndex = " + fromIndex);
            if (toIndex > list.size())
                throw new IndexOutOfBoundsException("toIndex = " + toIndex);
            if (fromIndex > toIndex)
                throw new IllegalArgumentException("fromIndex(" + fromIndex +
                                                   ") > toIndex(" + toIndex + ")");
            l = list;
            offset = fromIndex;
            size = toIndex - fromIndex;
            this.modCount = l.modCount;
        }
    
        public E set(int index, E element) {
            rangeCheck(index);
            checkForComodification();
            return l.set(index+offset, element);//可以通过更改subList来设置原容器对象
        }
    ....}
  • 相关阅读:
    Android学习第九天
    Android短信备份及插入笔记
    内容提供者实现应用访问另一个应用的数据库
    Verilog语言实现1/2分频
    QT中一个界面向另一个界面发送信号
    CMAKE设置Windows SDK编译版本
    VS2017下载地址
    VS 设置Windows SDK版本
    OBS 64bit版本编译
    Qt打包程序无法运行,提示应用程序无法正常启动0xc000007b解决办法
  • 原文地址:https://www.cnblogs.com/hlhdidi/p/5905823.html
Copyright © 2020-2023  润新知