1.集合框架是什么?
在Java語言中,Java語言的設計者對常用的數據結構和算法做了一些規范(接口)和實現(具體實現接口的類)。所有抽象出來的數據結構和操作(算法)統稱為Java集合框架(Java Collection Framework)。Java程序員在具體應用時,不必考慮數據結構和算法實現細節,只需要用這些類創建出來一些對象,然后直接應用就可以了。這樣就大大提高了編程效率。
Java 2集合框架圖
集合接口:6個接口(短虛線表示),表示不同集合類型,是集合框架的基礎。
抽象類:5個抽象類(長虛線表示),對集合接口的部分實現。可擴展為自定義集合類。
實現類:8個實現類(實線表示),對接口的具體實現。
在很大程度上,一旦您理解了接口,您就理解了框架。雖然您總要創建接口特定的實現,但訪問實際集合的方法應該限制在接口方法的使用上;因此,允許您更改基本的數據結構而不必改變其它代碼。
Java 2簡化集合框架圖
- Collection 接口是一組允許重復的對象。
- Set 接口繼承 Collection,但不允許重復,使用自己內部的一個排列機制。
- List 接口繼承 Collection,允許重復,以元素安插的次序來放置元素,不會重新排列。
- Map接口是一組成對的鍵值對象,即所持有的是key-value pairs。Map中不能有重復的key。擁有自己的內部排列機制。
- 容器中的元素類型都為Object。從容器取得元素時,必須把它轉換成原來的類型。
2.collection常用接口
boolean add(E e)
確保此 collection 包含指定的元素(可選操作)。
boolean addAll(Collection<? extends E> c)
將指定 collection 中的所有元素都添加到此 collection 中(可選操作)。
void clear()
移除此 collection 中的所有元素(可選操作)。
boolean contains(Object o)
如果此 collection 包含指定的元素,則返回 true。
boolean containsAll(Collection<?> c)
如果此 collection 包含指定 collection 中的所有元素,則返回 true。
boolean equals(Object o)
比較此 collection 與指定對象是否相等。
int hashCode()
返回此 collection 的哈希碼值。
boolean isEmpty()
如果此 collection 不包含元素,則返回 true。
Iterator<E> iterator()
返回在此 collection 的元素上進行迭代的迭代器。
boolean remove(Object o)
從此 collection 中移除指定元素的單個實例,如果存在的話(可選操作)。
boolean removeAll(Collection<?> c)
移除此 collection 中那些也包含在指定 collection 中的所有元素(可選操作)。
boolean retainAll(Collection<?> c)
僅保留此 collection 中那些也包含在指定 collection 的元素(可選操作)。
int size()
返回此 collection 中的元素數。
Object[] toArray()
返回包含此 collection 中所有元素的數組。
<T> T[] toArray(T[] a)
返回包含此 collection 中所有元素的數組;返回數組的運行時類型與指定數組的運行時類型相同。
所有實現了Collection接口的容器類都有一個為其子接口的iterator方法用以返回一個實現了Iterator接口的對象Iterator對象稱作迭代器,用以方便的實現對容器內元素的遍歷操作.
Iterator接口定義了如下方法:
boolean hasNext()
如果仍有元素可以迭代,則返回 true。
E next()
返回迭代的下一個元素。
void remove()
從迭代器指向的 collection 中移除迭代器返回的最后一個元素(可選操作)。
下面舉例說明Collection和迭代器的一些常見用法:
/**
* 測試Collection接口中方法的常見用法
* 測試迭代器的簡單用法
*/
package com.basic.collection;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
/**
* @author johnston678
*
* @version 2009-05-06
*/
public class TestCollection {
/**
* @param args
*/
public static void main(String[] args) {
Collection c = new HashSet();
c.add("hello");
//c.add(new Integer(100));
System.out.println(c.size());
c.remove("hello");
System.out.println(c);
c.add("Tom");
c.add("Jack");
Iterator i = c.iterator();
while (i.hasNext()) {
//next()返回值為Object類型,需要轉換為相應的類型
String str = (String) i.next();
System.out.println(str);
}
}
}
以上的while也可以使用高級的for循環:
for(string str :c) {
System.out.println("elements: " + str);
}
3.擴展的collection--set接口的實現
以處理集合,其中元素必須唯一。
Set實現可分為通用Set實現和專有Set實現兩類。
通用Set實現
Java提供了三個通用 Set 實現—— HashSet,TreeSet和LinkedHashSet。
HashSet采用散列函數對元素進行排序,是專門為快速查詢而設計的。存入HashSet的對象必須定義hashCode()。
值得注意的是:由于 HashSet在內存中數據空間為線性分配。因此,如果初始的容量過大則會浪費大量的空間和時間;如果初始容量過小將會因為增容而不得不復制已有的數據集到新緩沖區中,從而浪費時間。如果你不指定初始容量,默認為16,當容量不足時以成倍的形式增加容量。下列代碼創建了一個初始容量為64的 HashSet:
Set<String> s = new HashSet<String>(64);
TreeSet采用紅黑樹的數據結構進行排序元素,能保證元素的次序,使用它可以從Set中提取有序的序列。
HashSet比TreeSet提供更快的訪問速度,但無法保證訪問順序。用HashSet還是TreeSet取決于你是否需要順序訪問。
LinkedHashSet以鏈表的形式實現哈希表,它能提按照插入順序迭代訪問。
專有Set實現
專有Set實現有兩種——EnumSet 和 CopyOnWriteArraySet。
EnumSet是為枚舉類的Set提供的高性能Set。其成員在內部表示為位向量,具有很好的空間和時間性能。其迭代器能按其自然順序(聲明枚舉常量的順序)遍歷這些元素。
EnumSet類提供了一個靜態的函數range,用以簡單的創建其實例:
for (Day d : EnumSet.range(Day.MONDAY, Day.FRIDAY))
System.out.println(d);
此外,也可通過of函數來創建包含指定枚舉的EnumSet:
EnumSet.of(Style.BOLD, Style.ITALIC)
CopyOnWriteArraySet 在內部通過copy-on-write數組實現。對Set的add, set, 及remove操作都會創建數組的副本。迭代操作甚至可以和插入刪除操作同時進行。和大多數Set實現不同,add, remove和 contains操作需要的時間和set的大小成比例。該實現僅適用于那些很少修改,而需要經常進行迭代操作的地方。
4.Map實現
通用Map 實現
通用Map實現有三個——HashMap,TreeMap和LinkedHashMap。
和Set實現類似,如果你需要進行SortedMap操作或按key大小順序迭代操作,或者快速得到第一個值或者第二個值,請使用TreeMap。如果你需要最快的查詢速度而不關心迭代順序,請使用HashMap。如果你需要接近HashMap的性能,同時需要按照插入順序迭代,請使用LinkedHashMap。
專有Map實現
專有Map實現有三個——EnumMap, WeakHashMap和IdentityHashMap。
EnumMap在內部以數組方式實現,能為枚舉提供高性能和線程安全的查詢操作。如果想實現枚舉類型的Map,EnumMap無疑是性能最佳的選擇。
WeakHashMap也是Map接口的一種實現,它的Key值存儲的只是對象的弱引用。這意味著:當某個鍵不再正常使用時,將自動移除其條目。更精確地說,對于一個給定的鍵,其映射的存在并不阻止垃圾回收器對該鍵的丟棄,這就使該鍵成為可終止的,被終止,然后被回收。丟棄某個鍵時,其條目從映射中有效地移除,因此,因此該類的行為與其他的 Map 實現有所不同。
IdentityHashMap通過哈希表實現 Map 接口,比較鍵(和值)時使用引用相等性代替對象相等性。在 IdentityHashMap 中,當且僅當 (k1==k2) 時,才認為兩個鍵 k1 和 k2 相等(在正常 Map 實現(如 HashMap)中,當且僅當滿足下列條件時才認為兩個鍵 k1 和 k2 相等:(k1==null ? k2==null : e1.equals(e2)))。
此類不是通用 Map 實現!此類實現 Map 接口時,它有意違反 Map 的常規協定,該協定在比較對象時強制使用 equals 方法。此類設計僅用于其中需要引用相等性語義的罕見情況。
此類的典型用法是拓撲保留對象圖形轉換,如序列化或深層復制。要執行這樣的轉換,程序必須維護用于跟蹤所有已處理對象引用的"節點表"。節點表一定不等于不同對象,即使它們偶然相等也如此。此類的另一種典型用法是維護代理對象。例如,調試設施可能希望為正在調試程序中的每個對象維護代理對象。
5.Queue
Queue是通過把對象添加(稱為enqueuing的操作) 到Queue的后部并通過從Queue的前部提取對象而從Queue中移除(稱為dequeuing的操作)來模擬。
add()向隊列中添加元素,出錯則拋出異常,offer()也是向隊列中添加元素,但是出錯時返回錯誤碼。
remove()取得隊列中的元素并且刪除該元素,出錯則拋出異常,offer()也是取得隊列中的元素并且刪除該元素,但是出錯時返回錯誤碼。
element()和poll()返回下一個元素,但是不刪除。
Queue有幾個子類是線程安全的,而有三個是不安全的,或者說適用于單線程的。先介紹這三個線程不安全的吧。
PriorityQueue--當你添加元素到Queue中時,實現了自動排序。根據你使用的PriorityQueue的不同構造器,Queue元素的順序要么基于他們的自然順序要么通過PriorirtyQueue構造器傳入的Comparator來確定。你可以指定大小,若你在乎效率的話,但是它其實是會自動改變大小的。
舉例:
import java.util.PriorityQueue;
public class CollectionThirteen {
public static void main(String arg[]) {
PriorityQueue<String> queue = new PriorityQueue<String>();
queue.add("charlie");
queue.add("baker");
queue.add("dog");
queue.add("fox");
queue.add("able");
queue.add("easy");
while(queue.size() > 0) {
String str = queue.poll();
System.out.println(str);
}
}
}
ArrayDeque--擴展AbstractCollection和實現Deque接口,允許動態雙端隊列,可以用迭代器,并且提供了先進后出的方法,可以用于模擬stack。而Stack是Vector的一個子類,它實現了標準的后進先出堆棧。在JDK5中更新為泛型。盡管Stack沒有被建議不使用,但隨著Java SE6的發布,ArrayDeque是一個更好的選擇。
例如:在本例中你要是想模擬stack,那么可以使用push方法壓入元素,然后用pop取出,否則就是先進先出
import java.util.ArrayDeque;
public class CollectionFourteen {
public static void main(String arg[]) {
ArrayDeque<String> queue = new ArrayDeque<String>();
queue.add("charlie");
queue.add("baker");
queue.add("dog");
queue.add("fox");
queue.add("able");
queue.add("easy");
while(queue.size() > 0) {
String str = queue.poll();
System.out.println(str);
}
}
}
LinkedList--擴展AbstractSequentialList實現鏈表。實現了List,Deque和Queue接口,可以實現鏈表和stack(因為有push和pop方法)。
例如:本例與上例基本一致,改變為stack的方式也相同。
import java.util.LinkedList;
public class CollectionFifteen {
public static void main(String arg[]) {
LinkedList<String> queue = new LinkedList<String>();
queue.add("charlie");
queue.add("baker");
queue.add("dog");
queue.add("fox");
queue.add("able");
queue.add("easy");
while(queue.size() > 0) {
String str = queue.poll();
System.out.println(str);
}
}
}
6.集合的轉化
以下這個例子是將TreeSet中的元素轉化為數組。
import java.util.TreeSet;
public class CollectionSixteen {
public static void main(String arg[]) {
new CollectionSixteen();
}
CollectionSixteen() {
TreeSet<String> set = new TreeSet<String>();
set.add("Orange");
set.add("Peach");
set.add("Apple");
set.add("Banana");
set.add("Strawberry");
set.add("Pear");
Object obj[] = set.toArray();
for(int i=0; i<obj.length; i++)
System.out.println((String)obj[i]);
}
}
這個其實等同于:
import java.util.TreeSet;
public class CollectionSeventeen {
public static void main(String arg[]) {
new CollectionSeventeen();
}
CollectionSeventeen() {
TreeSet<String> set = new TreeSet<String>();
set.add("Orange");
set.add("Peach");
set.add("Apple");
set.add("Banana");
set.add("Strawberry");
set.add("Pear");
String str[] = new String[set.size()];
set.toArray(str);
for(int i=0; i<str.length; i++)
System.out.println(str[i]);//與上例唯一的不同就是在使用的時候不用在類型轉換了。
}
}
我們再看一個關于HashSet到TreeSet的轉化:
import java.util.HashSet;
import java.util.TreeSet;
public class CollectionEighteen {
public static void main(String arg[]) {
new CollectionEighteen();
}
CollectionEighteen() {
HashSet<String> hashSet = new HashSet<String>();
hashSet.add("Agamemnon");
hashSet.add("Cato");
hashSet.add("Socrates");
hashSet.add("Plato");
hashSet.add("Zeno");
hashSet.add("Thucydides");
hashSet.add("Archimedes");
for(String str : hashSet)
System.out.println(str);
System.out.println();
TreeSet<String> treeSet = new TreeSet<String>(hashSet);
for(String str : treeSet)
System.out.println(str);
}
}
7.線程安全
ConcurrentSkipListMap--實現ConcurrentNavigableMap接口,可量測的,并發的,可跳躍的列表實現。
ConcurrentHashMap--該實現提供了相同的基本線程安全的 Map 功能,但它大大提高了并發性。
ConcurrentLinkedQueue--是基于鏈接節點的、線程安全的隊列。并發訪問不需要同步。因為它在隊列的尾部添加元素并從頭部刪除它們(先進先出),所以只要不需要知道隊列的大小(自動調整大小),ConcurrentLinkedQueue對公共集合的共享訪問就可以工作得很好。收集關于隊列大小的信息會很慢,需要遍歷隊列。
SynchronousQueue--是最簡單的。它沒有內部容量。它就像線程之間的手遞手機制。在隊列中加入一個元素的生產者會等待另一個線程的消費者。當這個消費者出現時,這個元素就直接在消費者和生產者之間傳遞,永遠不會加入到阻塞隊列中。可以有多個生產者和消費者,但是都會被自動同步。
LinkedBlockingQueue--一個由鏈接節點支持的可選有界隊列。當一個消費者試圖讀取一個空的LinkedBlockingQueue時,它不返回,read方法阻塞等待生產者的出現。
例如:
import java.util.concurrent.LinkedBlockingQueue;
public class CollectionNineteen {
public static void main(String arg[]) {
LinkedBlockingQueue<Integer> queue = new LinkedBlockingQueue<Integer>();
Sender sender = new Sender(queue);
sender.start();
Receiver receiver = new Receiver(queue);
receiver.start();
}
}
--------------------------------------
import java.util.concurrent.LinkedBlockingQueue;
public class Sender extends Thread {
LinkedBlockingQueue<Integer> queue;
Sender(LinkedBlockingQueue<Integer> queue) {
this.queue = queue;
}
public void run() {
while(true) {
for(int i=0; i<=5; i++) {
Integer integer = new Integer(i);
System.out.println("Sender: " + integer);
queue.add(integer);
try {
sleep(2000L);
} catch(InterruptedException e) {
return;
}
}
}
}
}
---------------------------------
import java.util.concurrent.TimeUnit;
import java.util.concurrent.LinkedBlockingQueue;
public class Receiver extends Thread {
LinkedBlockingQueue<Integer> queue;
Receiver(LinkedBlockingQueue<Integer> queue) {
this.queue = queue;
}
public void run() {
while(true) {
Integer integer;
try {
integer = queue.poll(60L,TimeUnit.SECONDS);
System.out.println("Received: " + integer);
sleep(integer.intValue() * 1000L);
} catch(InterruptedException e) {
return;
}
}
}
}
+++++++++++++++++++++++++++++++++++++++++++++++++++++
ArrayBlockingQueue--一個由數組支持的有界隊列。ArrayBlockingQueue 和 LinkedBlockingQueue 幾乎相同,只是在底層存儲實現方面有所不同,LinkedBlockingQueue 并不總是有容量界限。無大小界限的 LinkedBlockingQueue 類在添加元素時永遠不會有阻塞隊列的等待(至少在其中有 Integer.MAX_VALUE 元素之前不會,這個時候采用的容量限制即是Integer.MAX_VALUE)。
參考文獻:http://www.cnblogs.com/TianFang/archive/2007/10/18/929646.html
更多文章、技術交流、商務合作、聯系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號聯系: 360901061
您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對您有幫助就好】元
