一、队列和栈
什么是队列?队列是一种只能在一端插入,另外一端删除的有序线性表,队列中第一个插入也就第一个被移除,所以队列是一种先进先出的线性表;
什么是栈?栈是一种有序线性表,只能在表的一端进行插入和删除,最后插入的元素被第一个删除,所以栈是一种后进先出的线性表;
接下来还是上图
二、栈源码
这里直接看源代码吧,因为用单链表以及数组实现,如果前几篇看懂了实现一个栈还是很简单的,那我们直接看起源码来吧;
还是老样子先看继承结构:
public class Stack<E> extends Vector<E>
这里看下Stack这个类继承Vector这个类,那么我们就需要跳进去看下这个类的实现
public class Vector<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable
看到这个结构我们一定会很熟悉一下就想到了ArryList这个接口,所以这里不做过多的强调一些方法,这里只说一些重点内容;
1).Vertor类所有方法都实现了同步,这里除去迭代的方法,因为每一个方法上都增加了synchronized这个关键字;
2).这里强调一下扩容的方法,这里还的看下这个构造函数,才能对比出差别
public Vector(int initialCapacity, int capacityIncrement) { super(); if (initialCapacity < 0) throw new IllegalArgumentException("Illegal Capacity: "+ initialCapacity); this.elementData = new Object[initialCapacity]; this.capacityIncrement = capacityIncrement; } private void grow(int minCapacity) { // overflow-conscious code int oldCapacity = elementData.length; int newCapacity = oldCapacity + ((capacityIncrement > 0) ? capacityIncrement : oldCapacity); if (newCapacity - minCapacity < 0) newCapacity = minCapacity; if (newCapacity - MAX_ARRAY_SIZE > 0) newCapacity = hugeCapacity(minCapacity); elementData = Arrays.copyOf(elementData, newCapacity); }
这个构造函数初始化了一个capacityIncrement变量这个影响了数组扩容的大小,如果不指定就是默认扩展一倍,看到这里如果看过的第一篇的读者就知道与ArryList的差别了,忘记的了也没关系,可以翻看一下,这里我们稍微延伸性下,由于每个操作都是同步的方法,肯定在操作会影响性能,但是我们通常进行的单一操作,不管是添加还是删除或者等等一系列操作,我们肯定还需要声明一个锁来保证操作的安全性,所以这就需要2个锁来解决线程的安全问题,想到这里大家就明白为什么Vector这个类被废弃了吧。接下来我们还是继续Stack这个类在Vector这个类上主要包括了栈的push和 pop 操作,以及取堆栈顶点的 peek方法、测试堆栈是否为空的 empty 方法、在堆栈中查找项并确定到堆栈顶距离的 search方法。
三、队列源码
public interface Queue<E> extends Collection<E>
同样是直接看源码吧,这里看到了Collection这个类,这里需要提一下Collections这2个是不同的类,Collection这个类是一个集合接口。它提供了对集合对象进行基本操作的通用接口方法。Collection接口在Java 类库中有很多具体的实现。Collection接口的意义是为各种具体的集合提供了最大化的统一操作方式。而这个Collections是一个包装类,它包含有各种有关集合操作的静态多态方法。此类不能实例化,就像一个工具类,服务于Java的Collection框架,关于这个类我后面会介绍下这个类的API,先说今天的重点吧,下图是Collection继承结构,图虽然有些不标准,相信大家基本上还是能看明白的。这里说下每个分支基本上能干什么事,对Connlection这个体系的集合有简单的明白;
首先说一下Iterable,可以通过这个获取一个Iterator,使用这个进行迭代,下面是接口里面包含的方法;
Iterator<T> iterator();
接下来说Collection,这个里面主要包含一些对集合操作的方法,另外还有进行遍历的方法;剩下的子类就是对其扩展这里说下之间的不同,List这个接口是有序集合,可以根据索引进行值的查找,另外List里面可以允许重复值的出现;下面将List不同的API列出来让大家更明朗一些,可能没有复制全,因为源码注释有点多,另外就是List还提供listIterator这个方法,这个继承Iterator这个类,是集合可以向前迭代。
E get(int index); E set(int index, E element); void add(int index, E element); E remove(int index); int indexOf(Object o); int lastIndexOf(Object o); ListIterator<E> listIterator(); ListIterator<E> listIterator(int index); List<E> subList(int fromIndex, int toIndex);
接下来是Set这个不允许保存重复的元素,Set在Collection的接口上没做什么变动,完全一样,SortedSet这个接口最要是一些排序接口,TreeSet主要实现了SortedSet这个接口,内部方法相对比较简单,主要通过Comparator这个接口进行实现一系列的接口;NavigableSet这个接口主要扩展SortedSet具有搜索元素的方法,这个里面的接口我感觉说不容易很没明白大家写个测试程序看下就可以,真的没什么搞头(我有测试程序大家想要可以私聊下我);
public interface SortedSet<E> extends Set<E> { //返回一个比较器 Comparator<? super E> comparator(); //返回2个元素之间的集合 SortedSet<E> subSet(E fromElement, E toElement); //返回小于该元素的集合 SortedSet<E> headSet(E toElement); //返回大于或者等于该元素的集合 SortedSet<E> tailSet(E fromElement); E first();//set中第一个元素 E last();//set中最后一个元素 }
最后来一下今天的主题Queue,这里主要说下注意的地方Queue使用时要尽量避免Collection的add()和remove()方法,而是要使用offer()来加入元素,使用poll()来获取并移出元素。它们的优点是通过返回值可以判断成功与否,add()和remove()方法在失败的时候会抛出异常。 如果要使用头部元素而不移出该元素,使用
element()或者peek()方法;接下来说下Deque这个接口,这个需要说下双端队列和队列的差别,队列只允许在一端插入,在另外一端删除,而双端队列是一种扩展,它可以在两端进行删除和插入;因为可以在两端访问所以是一种具有栈和队列性质的数据结构,这里需要说一下Stack这个接口因为是同步线程,我们在选择实现队列的时候应该优先选择Deque这个接口,下面可以列一个简单的对应关系,让大家明白这个接口内部方法与队列和接口的对应关系;
四、结束语
其实队列和栈里面的东西还有好多应用场景,以后有遇到再说喽,等等把map这些都说完的时候我做一个完整的集合的继承结构,接下来就是树喽,最近还有想法写下SSH,java我也进入到框架学习了,其实我这边还有好多.net MVC的介绍都是写了一半。