JAVA集合框架9---前面的集合框架总结

前面我们分析的集合框架都是基于Collection这条主线,Collection定义了一个集合接口,集合之间没有顺序和位置的概念,可以把Collection想象成一个背包,背包里面装着元素。Collection描述了一个数据集合应该具有的操作,比如最基本的增加一个元素:add(E e),删除一个元素:remove(Object o),以及返回集合元素的个数:size(),判断是否包含某个元素contains(Object o),遍历整个集合iterator()等等。

接着我们分析了AbstractCollection类,AbstractCollection是一个抽象类,它通过迭代器实现了Collection接口的部分方法,目的就是加快开发一个Collection子类时的速度。如果我们想开发一个Collection类的子类,我们有两种选择,一种是实现Collection接口的所有方法,另一种是继承AbstractCollection并重写我们需要的方法。

接着我们分析了List接口,List接口继承自Collection接口,也可以说是扩展了Collection接口。List接口与Collection之间的区别就是,List定义的集合是由顺序概念的,调用者可以精确地控制在List集合内元素插入的位置,根据索引返回对应位置的元素。相较于Collection接口,List接口扩展的方法主要有:访问对应索引位置处的元素:get(int index);修改对应索引位置的元素 set(int index, E element) ;在指定索引处插入元素add(int index, E element);删除指定索引处的元素 remove(int index);返回对应元素的索引 indexOf(Object o) ;可以向前向后迭代的List迭代器listIterator()。

接着我们分析了AbstractList类,List与AbstractList之间的关系和Collection接口与AbstractCollection类的关系一样,AbstractList类是一个抽象类,提供了List接口的默认实现,这些实现都是基于List的迭代器来完成了。同样我们想要实现List接口也有两种选择:1、实现List接口的所有方法。2、继承AbstractList抽象类,并重写我们需要的方法。

接着我们分析了List接口的一个实现类ArrayList的源码,发现其底层实现就是一个可变长数组,有两个重要的成员变量:记录数据的elementData与记录集合大小的size,默认大小是10,每次扩容为原来的1.5倍。底层的实现方法也都比较简单。

接着我们分析了List接口的另一实现类LinkedList,与ArrayList不同的是,LinkedList底层实现是一个双向链表,LinkedList除了实现了List接口之外,还实现了双端队列Deque接口(Deque接口里面还定义了栈的常用操作:push、pop、peek),因此LinkedList除了可以当做List使用之外,还可以当做栈、队列以及双端对列来使用。LinkedList的底层源码基本就是双向链表的一些基本操作,页算是比较简单的了。

接着我们分析了双端对列Deque的另一个实现类ArrayDeque,LInkedList也实现了Deque,它是基于双向链表来实现的。而ArrayDeque是基于数组来实现的,这个数组是一个逻辑上循环的数组,核心就是有一个指向队头的指针head,和一个指向队尾的指针tail。与ArrayList一样,底层同样是可变长数组,但是这两个可变长数组之间还是有蛮大的差别的。ArrayList底层的可变长数组比较自由,而ArrayDeque的可变长数组就加了一些限制:1、数组最小长度为8,ArrayList没有限制。2、数组默认大小为16,ArrayList默认大小为10。3、数组的大小必须是2的幂次倍(即使初始化时传入的初始化大小不是2的幂次倍),ArrayList没有这样的要求。(这样做的原因是为了用位运算代替复杂的取模操作)4、数组每次扩容为原来数组的两倍,ArrayList每次扩容为原来的1.5倍。明白了循环数组的实现原来,ArrayDeque的源码就比较简单了。

接着我们分析了队列接口Queue的一个实现类PriorityQueue(队列接口Queue与双端队列接口Deque之间的区别:我们知道队列Queue是一种先进先出(FIFO)的数据结构,只允许在队头插入元素,队尾删除元素;双端队列接口Deque继承(扩展)了Queue接口,它允许在队头和队尾增加和删除元素,还添加了栈的方法)。与普通的队列不同,PriorityQueue保存的元素有一个优先级的概念,队头元素的优先级最大,即使它是后加入进来的,也会先出去。JDK中PriorityQueue的实现原理是最小二叉堆,二叉堆就是一棵完全二叉树,由于完全二叉树的特殊性质,我们可以使用数组来储存,实现最小二叉退的核心就是要明白向下调整(siftDown)和向上调整(siftUp)操作。如果知道了这两点,PriorityQueue的源码也就好理解了。同样PriorityQueue底层储存数据也是使用可变长数组来实现的,默认大小为11,当数组长度小于64时,每次扩容2倍左右(2*oldCapacity + 2),大于64时每次扩容1.5倍。

猜你喜欢

转载自blog.csdn.net/qq_22158743/article/details/87917157