**java 中幾種常用數據結構**
一、數據結構起源
1968年,美國 Donald E. Knuth 教授在《計算機程序設計藝術》第一卷《基本算法》中系統闡述了數據的邏輯結構和存儲結構及其操作,開創了數據結構課程體系。
70年代初,大型程序相繼出現,軟件也開始相對獨立,結構程序設計成爲程序設計方法學主要內容,人們開始認爲程序設計的實質是對確定的問題選擇一種好的結構,加上設計一種好的算法。也就是 程序設計 = 數據結構 + 算法(尼古拉斯·沃斯(Niklaus Wirth) 提出)
現實生活中,更多的不是解決一些數值計算問題,我們需要通過表、樹、圖等數據結構的幫助來更好地處理問題。
所以數據結構也是一門研究非數字計算的程序設計問題中的操作對象,以及他們之間的關係和操作等相關問題的學科。(引用自《大話數據結構》)
二、基本概念和術語
1、數據
數據指的是能輸入到計算機中,並能被計算機程序處理的對象。
對於數值類型(整型、實型等),可以進行數值計算;
對於字符數據類型(聲音、圖像、視頻等可通過編碼轉化爲字符數據),可以進行非數值處理。
2、數據元素
數據元素指組成數據的、有意義的基本單位,也被稱爲記錄。
如:一部電影裏面的女主就是數據元素
3、數據項
數據項是數據不可分割的最小單位,一個數據元素可以由若干數據項組成。
如:一部電影裏面女主的姓名、性別等都是數據項,恩,女主性別一般是女…
4、數據對象
數據對象指性質相同的數據元素的集合,是數據的子集;
數據對象簡稱數據
什麼是性質相同呢?
性質相同指數據元素具有相同數量和類型的數據項;
如一部電影中每個角色(數據元素)都有姓名、性別(數據項),這些角色(數據元素)構成了一部電 影,那麼這部電影所有人物的集合我們可以想象成是一個數據對象。
5、數據結構
數據結構指互相之間存在一種或多種特定關係的數據元素的集合;
數據結構 = 數據元素 + 關係;
還得用上面的例子,一部電影可以想象成是一個數據結構,是由一個個人物(數據元素)和一個個串聯的人物情節 (關係)構成,例子有點勉強,大概意思都應該理解的.
java中有幾種常用的數據結構,主要分爲Collection和map兩個主要接口(接口只提供方法,並不提供實現),而程序中最終使用的數據結構是繼承自這些接口的數據結構類。其主要的關係(繼承關係)有: (—-詳細參見java api文檔!)
Collection和Map的區別在於容器中每個位置保存的元素個數:
Java容器類的用途是“保存對象”,分爲兩類:Map——存儲“鍵值對”組成的對象;Collection——存儲獨立元素。Collection又可以分爲List和Set兩大塊。List保持元素的順序,而Set不能有重複的元素。
1) Collection 每個位置只能保存一個元素(對象)
2) Map保存的是”鍵值對”,就像一個小型數據庫。我們可以通過”鍵”找到該鍵對應的”值”
Collection—->Collections
Map—–>SortedMap——>TreeMap
Collection—->List—–>(Vector \ ArryList \ LinkedList) Map——>HashMap
Collection—->Set——>(HashSet \ LinkedHashSet \ SortedSet)
圖片來源:http://blog.csdn.net/u010947402/article/details/51878166
Set
|——SortedSet接口——TreeSet實現類
Set接口——|——HashSet實現類
|——LinkedHashSet實現類
HashSet
HashSet有以下特點
不能保證元素的排列順序,順序有可能發生變化
不是同步的
集合元素可以是null,但只能放入一個null
當向HashSet集合中存入一個元素時,HashSet會調用該對象的hashCode()方法來得到該對象的hashCode值,然後根據 hashCode值來決定該對象在HashSet中存儲位置。
簡單的說,HashSet集合判斷兩個元素相等的標準是兩個對象通過equals方法比較相等,並且兩個對象的hashCode()方法返回值相等
注意,如果要把一個對象放入HashSet中,重寫該對象對應類的equals方法,也應該重寫其hashCode()方法。其規則是如果兩個對象通過equals方法比較返回true時,其 hashCode也應該相同。另外,對象中用作equals比較標準的屬性,都應該用來計算 hashCode的值。
TreeSet
TreeSet類型是J2SE中唯一可實現自動排序的類型
TreeSet是SortedSet接口的唯一實現類,SortedSet接口主要用於排序操作,即實現此接口的子類都屬於排序的子類。TreeSet可以確保集合元素處於排序狀態。TreeSet支持兩種排序方式,自然排序 和定製排序,其中自然排序爲默認的排序方式。向 TreeSet中加入的應該是同一個類的對象。
TreeSet判斷兩個對象不相等的方式是兩個對象通過equals方法返回false,或者通過CompareTo方法比較沒有返回0
自然排序
自然排序使用要排序元素的CompareTo(Object obj)方法來比較元素之間大小關係,然後將元素按照升序排列。
Java提供了一個Comparable接口,該接口裏定義了一個compareTo(Object obj)方法,該方法返回一個整數值,實現了該接口的對象就可以比較大小。
obj1.compareTo(obj2)方法如果返回0,則說明被比較的兩個對象相等,如果返回一個正數,則表明obj1大於obj2,如果是 負數,則表明obj1小於obj2。
如果我們將兩個對象的equals方法總是返回true,則這兩個對象的compareTo方法返回應該返回0
定製排序
自然排序是根據集合元素的大小,以升序排列,如果要定製排序,應該使用Comparator接口,實現 int compare(To1,To2)方法
LinkedHashSet
LinkedHashSet集合同樣是根據元素的hashCode值來決定元素的存儲位置,但是它同時使用鏈表維護元素的次序。這樣使得元素看起 來像是以插入順 序保存的,也就是說,當遍歷該集合時候,LinkedHashSet將會以元素的添加順序訪問集合的元素。
LinkedHashSet在迭代訪問Set中的全部元素時,性能比HashSet好,但是插入時性能稍微遜色於HashSet。
有許多人學了很長時間的Java,但一直不明白hashCode方法的作用,
我來解釋一下吧。首先,想要明白hashCode的作用,你必須要先知道Java中的集合。
Set總結:
(1)Set實現的基礎是Map(HashMap)(2)Set中的元素是不能重複的,如果使用add(Object obj)方法添加已經存在的對象,則會覆蓋前面的對象
java的HashCode方法
總的來說,Java中的集合(Collection)有兩類,一類是List,再有一類是Set。
你知道它們的區別嗎?前者集合內的元素是有序的,元素可以重複;後者元素無序,但元素不可重複。
那麼這裏就有一個比較嚴重的問題了:要想保證元素不重複,可兩個元素是否重複應該依據什麼來判斷呢?
這就是Object.equals方法了。但是,如果每增加一個元素就檢查一次,那麼當元素很多時,後添加到集合中的元素比較的次數就非常多了。 也就是說,如果集合中現在已經有1000個元素,那麼第1001個元素加入集合時,它就要調用1000次equals方法。這顯然會大大降低效率。
於是,Java採用了哈希表的原理。哈希(Hash)實際上是個人名,由於他提出一哈希算法的概念,所以就以他的名字命名了。 哈希算法也稱爲散列算法,是將數據依特定算法直接指定到一個地址上。如果詳細講解哈希算法,那需要更多的文章篇幅,我在這裏就不介紹了。
初學者可以這樣理解,hashCode方法實際上返回的就是對象存儲的物理地址(實際可能並不是)。 這樣一來,當集合要添加新的元素時,先調用這個元素的hashCode方法,就一下子能定位到它應該放置的物理位置上。 如果這個位置上沒有元素,它就可以直接存儲在這個位置上,不用再進行任何比較了;如果這個位置上已經有元素了, 就調用它的equals方法與新元素進行比較,相同的話就不存了,不相同就散列其它的地址。 所以這裏存在一個衝突解決的問題。這樣一來實際調用equals方法的次數就大大降低了,幾乎只需要一兩次。 所以,Java對於eqauls方法和hashCode方法是這樣規定的:
1、如果兩個對象相同,那麼它們的hashCode值一定要相同;
2、如果兩個對象的hashCode相同,它們並不一定相同
上面說的對象相同指的是用eqauls方法比較。你當然可以不按要求去做了,但你會發現,相同的對象可以出現在Set集合中。同時,增加新元素的效率會大大下降。
hashcode這個方法是用來鑑定2個對象是否相等的。 那你會說,不是還有equals這個方法嗎? 不錯,這2個方法都是用來判斷2個對象是否相等的。但是他們是有區別的。 一般來講,equals這個方法是給用戶調用的,如果你想判斷2個對象是否相等,你可以重寫equals方法,然後在代碼中調用,就可以判斷他們是否相等 了。簡單來講,equals方法主要是用來判斷從表面上看或者從內容上看,2個對象是不是相等。
舉個例子,有個學生類,屬性只有姓名和性別,那麼我們可以 認爲只要姓名和性別相等,那麼就說這2個對象是相等的。
hashcode方法一般用戶不會去調用,比如在hashmap中,由於key是不可以重複的,他在判斷key是不是重複的時候就判斷了hashcode 這個方法,而且也用到了equals方法。這裏不可以重複是說equals和hashcode只要有一個不等就可以了!所以簡單來講,hashcode相 當於是一個對象的編碼,就好像文件中的md5,他和equals不同就在於他返回的是int型的,比較起來不直觀。我們一般在覆蓋equals的同時也要 覆蓋hashcode,讓他們的邏輯一致。
舉個例子,還是剛剛的例子,如果姓名和性別相等就算2個對象相等的話,那麼hashcode的方法也要返回姓名 的hashcode值加上性別的hashcode值,這樣從邏輯上,他們就一致了。 要從物理上判斷2個對象是否相等,用==就可以了。
Set相關實現類的代碼如下:
public class TestSet {
public static void main(String[] args) {
// Set<String> set = new HashSet<String>();
//
// insert(set);
// System.out.print(set);
// /**
// * 遍歷方法一,迭代遍歷
// */
// System.out.println();
// for(Iterator<String> iterator = set.iterator();iterator.hasNext();){
// System.out.print(iterator.next()+"---");
// }
//
//
// System.out.println();
// System.out.println("********************");
// /**
// * for增強循環遍歷
// */
// for(String value : set){
// System.out.print(value+"---");
// }
System.out.println();
System.out.println("************");
testTreeSet();
System.out.println();
System.out.println("************");
testHashSet();
System.out.println();
System.out.println("************");
testLinkedHashSet();
}
/**
* Created at 2017/10/12 by xxx
* description 添加元素
*/
public static void insert(Set set){
set.add("18");
set.add("23");
set.add("14");
set.add("34");
set.add("53");
set.add("44");
set.add("34");//在Set中的對象元素不能重複
set.add("13");
// set.add(null);
}
public static void testTreeSet(){
Set<String> treeSet = new TreeSet<String>();
insert(treeSet);
System.out.println("treeSet添加元素後順序輸出:"+treeSet);
}
public static void testHashSet(){
Set<String> hashSet=new HashSet<String>();
insert(hashSet);
System.out.println("hashSet添加元素後的無序輸出:"+hashSet);
}
public static void testLinkedHashSet(){
Set<String> linkedSet = new LinkedHashSet<String>();
linkedSet.add("First");
linkedSet.add("Second");
linkedSet.add("Thrid");
linkedSet.add("Fourth");
System.out.println("LinkedHashSet:"+linkedSet);//按照數據插入的順序輸出
Set<String> hashSet = new HashSet<String>();
hashSet.add("First");
hashSet.add("Second");
hashSet.add("Thrid");
hashSet.add("Fourth");
System.out.println("HashSet:"+hashSet);
}
}
————–Collection—————-
1、Collections
2、List
List是有序的Collection,使用此接口能夠精確的控制每個元素插入的位置。用戶能夠使用索引(元素在List中的位置,類似於數組下 >標)來訪問List中的元素,這類似於Java的數組。
3、Vector
基於數組(Array)的List,其實就是封裝了數組所不具備的一些功能方便我們使用,所以它難易避免數組的限制,同時性能也不可能超越數組。所以,在可能的情況下,我們要多運用數組。另外很重要的一點就是Vector是線程同步的(sychronized)的,這也是Vector和ArrayList 的一個的重要區別。
4、ArrayList
同Vector一樣是一個基於數組上的鏈表,但是不同的是ArrayList不是同步的。所以在性能上要比Vector好一些,但是當運行到多線程環境中時,可需要自己在管理線程的同步問題。
5、LinkedList
LinkedList不同於前面兩種List,它不是基於數組的,所以不受數組性能的限制。
它每一個節點(Node)都包含兩方面的內容:
1.節點本身的數據(data);
2.下一個節點的信息(nextNode)。
所以當對LinkedList做添加,刪除動作的時候就不用像基於數組的ArrayList一樣,必須進行大量的數據移動。只要更改nextNode的相關信息就可以實現了,這是LinkedList的優勢。
List總結:
所有的List中只能容納單個不同類型的對象組成的表,而不是Key-Value鍵值對。例如:[ tom,1,c ]
所有的List中可以有相同的元素,例如Vector中可以有 [ tom,koo,too,koo ]
所有的List中可以有null元素,例如[ tom,null,1 ]
基於Array的List(Vector,ArrayList)適合查詢,而LinkedList 適合添加,刪除操作
————說明———–
一、幾個常用類的區別
1.ArrayList: 元素單個,效率高,多用於查詢
2.Vector: 元素單個,線程安全,多用於查詢
3.LinkedList:元素單個,多用於插入和刪除
4.HashMap: 元素成對,元素可爲空
5.HashTable: 元素成對,線程安全,元素不可爲空
二、Vector、ArrayList和LinkedList
大多數情況下,從性能上來說ArrayList最好,但是當集合內的元素需要頻繁插入、刪除時LinkedList會有比較好的表現,但是它們三個性能都比不上數組,另外Vector是線程同步的。所以:
如果能用數組的時候(元素類型固定,數組長度固定),請儘量使用數組來代替List;
如果沒有頻繁的刪除插入操作,又不用考慮多線程問題,優先選擇ArrayList;
如果在多線程條件下使用,可以考慮Vector;
如果需要頻繁地刪除插入,LinkedList就有了用武之地;
三、Collections的相關算法
binarySearch:折半查找。
sort:排序,這裏是一種類似於快速排序的方法,效率仍然是O(n * log n),但卻是一種穩定的排序方法。
reverse:將線性表進行逆序操作
rotate:以某個元素爲軸心將線性表“旋轉”。
swap:交換一個線性表中兩個元素的位置。
……
List的相關實現類代碼如下:
public class TestList {
/**
* 初始化一個List
* @param list
*/
public static void init(List list){
if(list != null){
list.add("aaa");
list.add("bbb");
list.add("ccc");
list.add("ddd");
list.add("eee");
}
}
/**
* 輸出List的內容
* @param list
*/
public static void output(List list){
if (list != null){
//根據列表下標遍歷,使用list.size()獲取列表中元素的個數
for (int i=0; i<list.size(); i++){
System.out.print(list.get(i) + " ");
}
//或者用迭代器遍歷
Iterator it = list.iterator();
Object value = null;
while (it.hasNext()){
value = it.next();
//System.out.println(value);
}
}
System.out.println();
}
/**
* 使用ArrayList
*/
public static void testArrayList(){
List list = new ArrayList();
init(list);
System.out.println("使用ArrayList: ");
output(list);
}
/**
* 使用Vector
*/
public static void testVector(){
List list = new Vector();
init(list);
System.out.println("使用Vector: ");
output(list);
}
/**
* 使用LinkedList
*/
public static void testLinkedList(){
List list = new LinkedList();
init(list);
System.out.println("使用LinkedList: ");
output(list);
}
public static void main(String[] args) {
TestList.testArrayList();
TestList.testVector();
TestList.testLinkedList();
List list = new ArrayList();
System.out.println(list == null);
init(list);
//List支持元素重複
list.add("aaa");
list.add("bbb");
System.out.println("插入元素aaa, bbb後:");
output(list);
//指定元素插入的位置
list.add(1, "fff");
System.out.println("在下標爲1處插入fff後:");
output(list);
List list2 = new ArrayList();
list2.add("ggg");
list2.add("hhh");
//將另一列表中的元素插入到列表中
list.addAll(list2);
System.out.println("添加list2的元素後:");
output(list);
//判斷列表是否包含某一元素
//通過元素的equals方法,判斷元素是否相等
System.out.println("list包含aaa? " + list.contains("aaa"));
//判斷列表中是否包含了另外一個列表中的所有元素。
System.out.println("list包含list2中的所有元素? " + list.containsAll(list2));
//定位一個元素在列表中最先出現的位置
System.out.println("aaa在list中第一次出現的位置: " + list.indexOf("aaa"));
//定位一個元素在列表中最後出現的位置
System.out.println("aaa在list中最後一次出現的位置: " + list.lastIndexOf("aaa"));
//更新列表中某個位置的元素值
list.set(2, "xxx");
System.out.println("更新位置爲2的元素爲xxx後:");
output(list);
//刪除列表中的某個元素,只刪除第一次出現的那個
list.remove("aaa");
System.out.println("刪除元素aaa後:");
output(list);
//刪除列表中指定位置的元素
list.remove(1);
System.out.println("刪除下標爲1的元素後:");
output(list);
//刪除列表除list2以外的其他元素
list.retainAll(list2);
System.out.println("刪除除list2包含的以外的元素後:");
output(list);
//刪除列表中在另一列表中也包含了的元素
list.removeAll(list2);
System.out.println("刪除list2包含的元素後:");
output(list);
//清空列表
list.clear();
//判斷列表中是否有數據
System.out.println("清空List後,list爲空麼? " + list.isEmpty());
init(list);
//用列表中的某些元素構造一個新的列表
list2 = list.subList(1,4);
System.out.println("用list的第1個到第4個元素構造一個新的List:");
output(list2);
//用List特有的遍歷器ListIterator遍歷列表
//與普通的Iterator不同,它允許兩個方向遍歷列表
ListIterator listIt = list.listIterator();
System.out.println("正向遍歷列表");
while (listIt.hasNext()){
System.out.print(listIt.next());
}
System.out.println();
System.out.println("反向遍歷列表");
while (listIt.hasPrevious()){
System.out.print(listIt.previous()+"-");
}
System.out.println();
//也可以使用ListIterator從List中間插入和刪除元素,
//只能在遍歷器當前位置添加和刪除。
listIt.add("newadd");
System.out.println("用ListIterator往列表中添加元素newadd後: ");
output(list);
listIt.next();
listIt.remove();//todo:刪除多個
System.out.println("用ListIterator刪除列表中元素後: ");
output(list);
listIt.next();//下標下移一位
listIt.remove();
System.out.println("用ListIterator刪除列表中元素後: ");
output(list);
//LinkedList自定義的方法
LinkedList linklist = new LinkedList();
init(linklist);
//添加元素到列表頭
linklist.addFirst("fff");
System.out.println("把fff放到列表頭後:");
output(linklist);
//添加元素到列表尾
linklist.addLast("eee");
System.out.println("把eee放到列表尾後:");
output(linklist);
//獲取表頭元素
System.out.println("列表頭元素:" + linklist.getFirst());
//獲取表尾元素
System.out.println("列表尾元素:" + linklist.getLast());
//刪除列表頭的元素
linklist.removeFirst();
System.out.println("刪除列表頭元素後:");
output(linklist);
//刪除列表尾的元素
linklist.removeLast();
System.out.println("刪除列表尾元素後:");
output(linklist);
List list3=new LinkedList<>();
list3.add("111");
list3.add("222");
linklist.addAll(4,list3);
System.out.println("在下標爲4處添加list3後:");
output(linklist);
//堆棧Stack類,它繼承自Stack類
Stack myStack = new Stack();
//插入元素,是插入到尾部
myStack.push("aaa");
myStack.push("bbb");
myStack.push("ccc");
myStack.push("ddd");
myStack.push("aaa");
myStack.push("ddd");
System.out.println("堆棧中的元素是: ");
output(myStack);
System.out.println("堆棧尾部元素: " + myStack.peek());
System.out.println("彈出堆棧尾部元素: " + myStack.pop());
output(myStack);
myStack.push("kkk");
output(myStack);
}
}
treeSet的倒敘代碼如下(繼承Comparator):
public class TreeSetTest2 {
public static void main(String[] args) {
Set<String> set = new TreeSet<String>(new PersonComparator());
set.add("a");
set.add("b");
set.add("c");
set.add("d");
set.add("e");
set.add("A");
// set.add("1");
// set.add("8");
// set.add("3");
// set.add("4");
// set.add("5");
// set.add("6");
// for(Iterator<Person> iterator = set.iterator();iterator.hasNext();){
// System.out.print(iterator.next().score+" ");
// }
System.out.println(set);
}
}
class PersonComparator implements Comparator<String>{
@Override
public int compare(String o1, String o2) {
return o2.compareTo(o1);//降序排列
// return o1.compareTo(o2);//升序排列
}
}
Map
HashMap和Hashtable的區別
2、TreeMap
TreeMap則是對鍵按序存放,因此它便有一些擴展的方法,比如firstKey(),lastKey()等,你還可以從TreeMap中指定一個範圍以取得其子Map。
鍵和值的關聯很簡單,用put(Object key,Object value)方法即可將一個鍵與一個值對象相關聯。用get(Object key)可得到與此key對象所對應的值對象。
1 HashMap不是線程安全的
HashMap是map接口的子類,是將鍵映射到值的對象,其中鍵和值都是對象,並且不能包含重複鍵,但可以包含重複值。HashMap允許null key和null value,而hashtable不允許。
2 HashTable是線程安全。
HashMap是Hashtable的輕量級實現(非線程安全的實現),他們都完成了Map接口,主要區別在於HashMap允許空(null)鍵值(key),由於非線程安全,效率上可能高於Hashtable。
HashMap允許將null作爲一個entry的key或者value,而Hashtable不允許。 HashMap把Hashtable的contains方法去掉了,改成containsvalue和containsKey。因爲contains方法容易讓人引起誤解。 Hashtable繼承自Dictionary類,而HashMap是Java1.2引進的Map interface的一個實現。 最大的不同是,Hashtable的方法是Synchronize的,而HashMap不是,在多個線程訪問Hashtable時,不需要自己爲它的方法實現同步,而HashMap 就必須爲之提供外同步。 Hashtable和HashMap採用的hash/rehash算法都大概一樣,所以性能不會有很大的差。
總結:
Map的相關實現類的代碼如下:
public class TestMap {
public static void main(String args[]) {
testTreeMap();
testHashMap();
testHashTable();//隨機順序
HashMap<String,Object> hashap =new HashMap<>();
hashap.put("as", new Integer(90));
hashap.put("yer", new Integer(30));
hashap.put("tt", new Integer(10));
hashap.put("we", new Integer(20));
hashap.put("ee", new Integer(60));
System.out.println("---"+hashap);
}
public static void testHashTable(){
Map map =new Hashtable();
addElement(map);
System.out.println("HashTable輸出:"+map);
}
public static void testTreeMap(){
Map map =new TreeMap();
addElement(map);
System.out.println("TreeMap輸出:"+map);
}
public static void testHashMap(){
Map map =new HashMap();
addElement(map);
System.out.println("HashMap輸出:"+map);
}
public static void addElement(Map map){
// map.put("1呀", new Integer(90));
// map.put("2呀", new Integer(30));
// map.put("4呀", new Integer(10));
// map.put("3呀", new Integer(20));
// map.put("5呀", new Integer(60));
// map.put("6呀", new Integer(80));
// map.put("7呀", new Integer(234));
// map.put("8呀", new Integer(14));
// map.put("9呀", new Integer(45));
// Map hashap =new HashMap();
// map.put(1, new Integer(90));
// map.put(3, new Integer(30));
// map.put(4, new Integer(10));
// map.put(5, new Integer(20));
// map.put(6, new Integer(60));
map.put("6呀", new Integer(80));
map.put("7呀", new Integer(234));
map.put("8呀", new Integer(14));
map.put("9呀", new Integer(45));
// map.put(null, null);
}
}
棧
在Java中Stack類表示後進先出(LIFO)的對象堆棧。棧是一種非常常見的數據結構,它採用典型的先進後出的操作方式完成的。每一個棧都包含一個棧頂,每次出棧是將棧頂的數據取出,如下:
圖片來源:http://blog.csdn.net/guofengpu/article/details/52092333
隊列
隊列(Queue):也是運算受限的線性表。是一種先進先出(First In First Out ,簡稱FIFO)的線性表。只允許在表的一端front進行插入,而在另一端rear進行刪除。
隊首(front) :允許進行刪除的一端稱爲隊首。
隊尾(rear) :允許進行插入的一端稱爲隊尾。
例如:排隊購物。操作系統中的作業排隊。先進入隊列的成員總是先離開隊列。
隊列中沒有元素時稱爲空隊列。在空隊列中依次加入元素a1, a2, …, an之後,a1是隊首元素,an是隊尾元素。顯然退出隊列的次序也只能是a1, a2, …, an ,即隊列的修改是依先進先出的原則進行的,如圖所示。
圖片來源:http://www.cnblogs.com/skywang12345/p/3562279.html
參考java常用數據結構來源:http://blog.csdn.net/u010947402/article/details/51878166
set不同實現類的區別:http://www.cnblogs.com/wl0000-03/p/6019627.html
map的不同實現類參考來源:http://www.cnblogs.com/langtianya/archive/2013/03/19/2970273.html
數據結構概念出處鏈接:http://www.jianshu.com/p/75425f405c25