Collection即单列集合。统一定义了一套单列集合的接口
由图中我们可以看出:
很直观。我们一看注释其实就能大概明白Java设计人员的思路,其实Iterable接口可以看成许多容器类共同特性被提取出来做了接口。而相对于不同容器,这个共有特性的具体实现细节又不同。既然不同所以又要自己定义自己系列的迭代器规则,并让所有实现类都遵循这个规则。
对于Collection接口来说,它规定子接口及其实现类都必须满足它重新设定的规则。
Returns an iterator over the elements in this collection. There are no guarantees concerning the order in which the elements are returned
针对于集合中的所有元素返回一个该集合的迭代器。 但是该迭代器无法保证元素间具有顺序性。(除非这个集合是某个具有这一特性的类的实例)
这里大家可以看Api文档,讲的比我自己理解的好很多。这里我只是想顺便锻炼一下自己看英文文献的能力。并且很多不懂的我都是照搬了Api文档
boolean add(E e)
向集合中添加一个元素。集合更改则添加成功返回true,如果该集合不允许重复并且已经包含指定的元素。返回false。部分子类的add方法可能会限制添加到集合中的元素类型,或者不会将NULL添加到集合中。
boolean addAll(Collection extends E> c)
将指定集合中的所有元素添加到此集合中。在添加过程中如果被添加的集合发生了更改,addAll方法不具有幂等性。
void clear()
清空掉集合中的所有元素
boolean contains(Object o)
如果集合中包含指定元素那么返回true。特别的,如果集合中也包含NULL元素的时候并且要查找的元素也是NULL的时候也返回true。
boolean containsAll(Collection> c)
如果该集合中包含指定集合中的所有元素的时候返回true。
boolean isEmpty()
如果集合中没有元素返回true。
boolean remove(Object o)
删除集合中的指定的元素。如果存在NULL,也删除。
boolean removeAll(Collection> c)
删除当前集合中所有等于指定集合中的元素。
boolean retainAll(Collection> c)
仅保留该指定集合中存在的所有元素。其余删除
int size()
返回该集合中元素的个数。如果超过了Integer.MAX_VALUE,那么返回Integer.MAX_VALUE。
Object[] toArray()
这个方法是集合和数组转化的桥梁。
见名知意,返回包含此集合中所有元素的数组。如果这个集合的迭代器保证元素有序,那么该方法与其迭代器中元素顺序一致。并且该方法返回的数组是拷贝出来的(某些集合底层数组实现,区别这个),可以进行任意的更改。
T[] toArray(T[] a)
该方法可以对返回的数组类型进行精确控制。而非像toArray方法一样返回Object[]
。
返回集合中所有元素到该数组中。如果这个数组可以容纳下的话,否则返回一个新new的数组,容量和集合中元素数量一致。如果指定的数组容量大于集合中元素个数,数组空闲位置填NULL。如果这个集合的Iterator具有顺序性的话,数组元素顺序与该迭代器一致。
新增抽象方法:
新增重写方法splIterator以及继承方法forEach。
从刚才的Colllection接口的结构体中我们可以看出,该Collection接口实现了Iterable接口,说明了Collection接口是可迭代的。而这个Iterable接口又只有一个iterator方法。返回值是Iterator对象。即返回的是对应实现类的迭代器对象。
如图,Iterator接口只有三个抽象函数。
首先我们先来对Iterator接口了解一下:
boolean hasNext()
如果迭代器中还有元素那么返回true。
E next()
返回迭代器中游标的下一元素
void remove()
从迭代器指向的 collection 中移除迭代器返回的最后一个元素。每次调用 next 后只能调用一次此方法。如果进行迭代时用调用此方法之外的其他方式修改了该迭代器所指向的 collection,则迭代器的行为是不确定的。
我们拿了一个Collection接口的实现类来看了一下。
ArrayList写了一个内部类Itr来实现Iterator接口作为自己的迭代器。
变量:
- cursor:迭代器下一个要返回的元素的索引
- lastRet:迭代器最后一个返回的元素的索引
- expectedModCount:期望中ArrayList集合的底层数组容器的修改次数,来和父类AbstractList中设定的变量modCount来进行比较。
方法:
- hasNext:判断游标是否指到了size即可。因为底层容器是数组所以size位置没有元素当游标指到的时候就是迭代器结束迭代的时候。
- next:首先判断容器是否在Itr初始化之后就已经被其他线程进行了更改。如果是那么就会根据checkForComodification方法的判断规则来跑出一个异常。然后就是通过游标来对容器进行迭代,并动态的修改lastRet的值。
- remove: 因为是更改操作,先判断一下容器是否被动过。然后调用该ArrayList类的实例的remove方法删除掉指定索引处的元素。此时动态的维护cursor和lastRet,expectedModCount变量。此处可有看出Iterator接口的remove方法的解释是什么意思了。
- checkForComodification:该方法就是简单的对expectedModCount变量和AbstractList类中的modCount变量进行判断看是否容器被其他线程更改了。这也就是为什么Iterator接口的remove方法的释义说迭代器的行为是不确定的。这里ArrayList由于是线程不安全的类,所以在其迭代器中设定了如果线程间冲突时的处理规则。
和Set集合最大的不同即红框中所述:
- 有序
- 允许重复元素
从Api列表中我们可以看到List接口定义了一个listIterator函数,返回一个ListIterator接口。该接口继承自Iterator接口,并提供了更多的函数。
从ListIterator接口的Api描述中总结出的几点就是:
- 相比于hasNext函数多了一个对应的hasPrevious函数,来检测游标指向的元素是否具有前驱元素。
- 相比于next函数多了一个previous函数,来获取游标指向的元素的前一位元素。
- 多了nextIndex和previousIndex函数,用来返回迭代器下一元素和前一迭代过的元素的索引。
- 多了set函数和add函数,set函数允许替换掉previous和next函数操作后返回的元素。add函数允许将元素插入到容器中游标前的位置。
相比于List集合最大的区别:
- 不包含重复元素。
这里我们强调一下,有些地方说Set集合是无序的,其实是不严谨的。可以看到jdk注释中是没有指明这一点的,那么List集合的注释中的有序和有些人常说的Set集合是无序的是什么意思呢?
首先要搞清楚、Java中有序和无序的概念:
有序指的是存储顺序与添加顺序相同,并且可以通过下标访问,List就是这样。
无序刚好相反,指的是存储顺序与添加顺序无关,没有下标,当然也不可能通过下标访问,Set就是如此。
这里需要注意的是,有序、无序中的“序”与我们平常所说的“顺序”无关。
而TreeSet是无序,但又是排好序的。即添加顺序与存储顺序无关,但是其中的对象实现了排序。
- HashSet:底层数据结构是哈希表(是一个元素为链表的数组)
- TreeSet:底层数据结构是红黑树(是一个自平衡的二叉树)。保证元素的排序方式
- LinkedHashSet:底层数据结构由哈希表和链表组成。