这里所有截图的源码来自JDK8
看一张图
首先看集合类的基本接口:Collection,它继承了Iterable这个接口。
看源码的时候发现这个接口下面既然出现了一个方法体,
这是由于JDK8中新增特性:接口允许定义非抽象方法,但必须用default来修饰。
在Collection有两个基本的方法
boolean add(E e);跟Iterator<E> iterator();
add就是向集合中添加元素,成功返回true,失败返回false(但是这个集合中不允许有重复的对象)
iterator方法用于返回Iterator接口的对象
这个接口对象中有3个我们所熟知的方法:hasNext,next,remove.
next方法可以使得我们逐个访问集合中每个元素,但是如果到达集合的末尾,next方法就会出现一个NoSuchElementException的异常,所以我们需要调用hasNext方法,在hasNext返回true的时候再反复调用next实现集合的遍历(这里我们也可以知道数组访问元素的方式是使用下标,而集合使用迭代器)。我们来看一下ArrayList中的一个内部类实现Iterator接口的方法Itr
如果i>=size 说明已经到达集合的末尾。
ConcurrentModificationException异常可能是因为,外部remove方法修改了modCount的值修改了,于expectedModCount的值
出现了不一致的情况。
再Java核心技术卷书中有这么一句话:Java迭代器查找操作于位置变更时紧密相连的,查找一个元素的唯一方法是调用next,
而在执行查找操作的同时,迭代器的位置也会随之向前移动,所以应将迭代器认为是位于两个元素之间,当调用next时,迭代
器就会越过下一个元素,并返回越过元素的引用。
Iterator接口remove方法会删除上次调用next方法返回的元素。所以:如果删除两个相连的元素我们需要先调用next返回需要删除
的对象。
下面来说明具体的集合:
目前为止,自己使用最多的ArrayList类(可以动态增长和缩减的索引序列)
它继承了AbstractList类,这个抽象类又继承了AbstractCollection类,这是因为Collection有许多方法,但是每一个类如果都
实现这么多方法失去原本意义,所以提供一个抽象类更好的实现Collection这个接口。
ArrayList的缺点,也可以说是数组的一个缺陷:对于删除的操作需要付出河大的代价,由于删除之后的元素
都需要前移,插入也会有同样的问题。但是链表却不存在这种问题,因为链表中的每个节点都是双向链接的(数组是在连续的位
置上存放对象的引用),正是由于节点存放着前驱节点的引用,所以在链表中进行删除和插入是很简单的,因为只需要更新附近
节点的的引用即可。
书籍上说:但是链表是一个有序集合,每一个对象的位置都很重要,所以,
LinkedList.add方法每次都是将值插入到链表的末尾:
从linkLast方法中可以看到新的节点每次都是在last节点链接在一起,如果last == null,则它作为first节点
那如何将元素插入到链表中间呢,使用迭代器(ListIterator),因为迭代器是用来表示集合中的位置,所以这种依赖位置
的add方法应由迭代器负责
List接口下有ListIterator方法。
这样我们可以实现linkedList中间插入的操作。
但是实际是:
我发现LinkedList中add方法包含了根据下标(可能下标不太准确)去增加value值的方法、那就来看一下源码:
就是如果插入位置是在末尾,那就直接调用linkLast方法进行末尾插入,但是如果不是末尾,那么我们就需要去执行一些操作:
首先看node(index):if跟else的判断是遍历方式从头到尾或者从尾到头。
再根据Node位置进行插入操作(其实质感觉还是依赖于下标的位置)
LinkedList的缺点:对于随机访问遍历链表效率很低:因为每次查找一个元素都需要从列表头部开始搜索。LinkedList对象
根本不做任何缓存位置信息的操作。