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来设置原容器对象
    }
....}
时间: 2024-10-12 02:10:20

Java源码初学_AbstractList&AbstractCollection的相关文章

Java源码初学_HashSet&amp;LinkedHashSet

一.概述 HashSet是建立在HashMap的基础上的,其内部存在指向一个HashMap对象的引用,操作HashSet实际上就是操作HashMap,而HashMap中所有键的值都指向一个叫做Dummy value的值.这是一个Object对象.是在HashSet进行显示初始化的时候初始化出来的,而HashMap在构造函数中被创建出来.二.HashMap在HashSet方法中的使用及LinkedHashMap HashSet是建立在HashMap的基础上的,它的几乎所有的操作都是通过HashMa

Java源码初学_LinkedList

一.LinkedList的内部数据结构 LinkedList底层是一个链表的数据结构,采用的是双向链表,基本的Node数据结构代码如下: private static class Node<E> { E item; //节点放置的元素 Node<E> next; //下一节点 Node<E> prev; //上一结点 Node(Node<E> prev, E element, Node<E> next) { this.item = element

Java源码初学_LinkedHashMap

一.概述: LinkedHashMap是HashMap的子类,它的基本操作与HashMap相同,与之不同的是,它可以实现按照插入顺序进行排序.也可以通过在构造函数中指定参数,按照访问顺序排序(LinkedHashSet无法按照访问顺序进行排序). 二.LinkedHashMap是如何实现按照插入顺序进行排序的? LinkedHashMap在Entry内部类中动了一点小手脚…实际上,LinkedHashMap所有的元素可以构成一个以header(Entry对象)为头的双向链表.在HashMap中有

Java源码初学_ArrayList

一.ArrayList的构造器和构造方法 在ArrayList中定义了两个空数组,分别对应当指定默认构造方法时候,指向的数组已经给定容量,但是容量等于0的时候,指向的数组.此外在构造函数中传入Collection对象的时候,直接调用对象的toArray方法,并且将容器内部的引用指向得到的数组.源代码如下: private static final long serialVersionUID = 8683452581122892189L; /** * 默认的数组的容量 */ private sta

Java集合框架源码阅读之AbstractCollection

AbstractCollection是集合实现类的根抽象实现类,它实现了Collection接口,集合中的三个分支Set.List.Queue都是继承此类之后再进行各自实现的扩展,分别是AbstractSet.AbstractList.AbstractQueue.这三个分支有一些共同之处,需要用一些共同的方法,因此出现了AbstractCollection类,它包含了一些这三个分支都会用到的常用方法.而这三个分支也各有抽象类,因为这三个分支下面的一些具体实现也会有一些当前分支通用的方法,因此也给

java源码解读--queue

queue接口特点:可以模拟队列行为,即"先进先出". 接口结构 queue接口继承了Collection接口,并增加了一些新方法 12345678910111213141516 public interface <E> extends Collection<E>{ boolean add(E e); //将元素插入队列,如果失败返回false boolean offer(E e); //移除并返回队列中的第一个元素,队列为空时,抛异常 E remove();

如何阅读Java源码 阅读java的真实体会

刚才在论坛不经意间,看到有关源码阅读的帖子.回想自己前几年,阅读源码那种兴奋和成就感(1),不禁又有一种激动. 源码阅读,我觉得最核心有三点:技术基础+强烈的求知欲+耐心. 说到技术基础,我打个比方吧,如果你从来没有学过Java,或是任何一门编程语言如C++,一开始去啃<Core Java>,你是很难从中吸收到营养的,特别是<深入Java虚拟机>这类书,别人觉得好,未必适合现在的你. 虽然Tomcat的源码很漂亮,但我绝不建议你一开始就读它.我文中会专门谈到这个,暂时不展开. 强烈

[笔记&amp;轮子]java源码 生成本地javadoc api文档

在用Eclipse写java代码时候,有时候因为不知道一个java函数的作用,会通过把鼠移动到java函数上,如果它有javadoc的相关内容就会显示出来.但是并非所有java代码都有javadoc:即使安装了javadoc,在eclipse中如果不进行设定,也可能无法使用. 我在win7下安装的是javase的jdk,发现eclipse中默认的javadoc路径是http://download.oracle.com/javase/7/docs/api/,显然这是一个在线资源,问题是网络总是不稳

一个android dex 转java源码工具

和dex2jar,smali2java比起来,这个工具至少结果是正确的,前两者对于循环,异常等的处理明显逻辑就是错误的. 该小工具是基于androguard制作,本来是想自己写一个,后来一找居然有现成的可以利用就简单添加功能. 用法如下 dexdecompile.exe dexfilename outputdirectory 比如 dexdecompile.exe  c:\classes.dex c:\test 运行完毕以后将会在c:\test生成所需要的java源码. 下载地址:百度网盘地址