JAVA的容器---List,Map,Set
Collection
├List
│├LinkedList
│├ArrayList
│└Vector
│ └Stack
└Set
Map
├Hashtable
├HashMap
└WeakHashMap
Collection接口
Collection是最基本的集合接口,一個Collection代表一組Object,即Collection的元素(Elements)。一些 Collection允許相同的元素而另一些不行。一些能排序而另一些不行。Java SDK不提供直接繼承自Collection的類,Java SDK提供的類都是繼承自Collection的“子接口”如List和Set。
所有實現Collection接口的類都必須提供兩個標準的構造函數:無參數的構造函數用于創建一個空的Collection,有一個 Collection參數的構造函數用于創建一個新的Collection,這個新的Collection與傳入的Collection有相同的元素。后一個構造函數允許用戶復制一個Collection。
如何遍歷Collection中的每一個元素?不論Collection的實際類型如何,它都支持一個iterator()的方法,該方法返回一個迭代子,使用該迭代子即可逐一訪問Collection中每一個元素。典型的用法如下:
Iterator it = collection.iterator(); // 獲得一個迭代子
while(it.hasNext()) {
Object obj = it.next(); // 得到下一個元素
}
由Collection接口派生的兩個接口是List和Set。
List接口
List是有序的Collection,使用此接口能夠精確的控制每個元素插入的位置。用戶能夠使用索引(元素在List中的位置,類似于數組下標)來訪問List中的元素,這類似于Java的數組。
和下面要提到的Set不同,List允許有相同的元素。
除了具有Collection接口必備的iterator()方法外,List還提供一個listIterator()方法,返回一個 ListIterator接口,和標準的Iterator接口相比,ListIterator多了一些add()之類的方法,允許添加,刪除,設定元素,還能向前或向后遍歷。
實現List接口的常用類有LinkedList,ArrayList,Vector和Stack。
LinkedList類
LinkedList實現了List接口,允許null元素。此外LinkedList提供額外的get,remove,insert方法在 LinkedList的首部或尾部。這些操作使LinkedList可被用作堆棧(stack),隊列(queue)或雙向隊列(deque)。
注意LinkedList沒有同步方法。如果多個線程同時訪問一個List,則必須自己實現訪問同步。一種解決方法是在創建List時構造一個同步的List:
List list = Collections.synchronizedList(new LinkedList(...));
ArrayList類
ArrayList實現了可變大小的數組。它允許所有元素,包括null。ArrayList沒有同步。
size,isEmpty,get,set方法運行時間為常數。但是add方法開銷為分攤的常數,添加n個元素需要O(n)的時間。其他的方法運行時間為線性。
每個ArrayList實例都有一個容量(Capacity),即用于存儲元素的數組的大小。這個容量可隨著不斷添加新元素而自動增加,但是增長算法并沒有定義。當需要插入大量元素時,在插入前可以調用ensureCapacity方法來增加ArrayList的容量以提高插入效率。
和LinkedList一樣,ArrayList也是非同步的(unsynchronized)。
Vector類
Vector非常類似ArrayList,但是Vector是同步的。由Vector創建的Iterator,雖然和ArrayList創建的 Iterator是同一接口,但是,因為Vector是同步的,當一個Iterator被創建而且正在被使用,另一個線程改變了Vector的狀態(例如,添加或刪除了一些元素),這時調用Iterator的方法時將拋出ConcurrentModificationException,因此必須捕獲該異常。
Stack 類
Stack繼承自Vector,實現一個后進先出的堆棧。Stack提供5個額外的方法使得Vector得以被當作堆棧使用。基本的push和pop 方法,還有peek方法得到棧頂的元素,empty方法測試堆棧是否為空,search方法檢測一個元素在堆棧中的位置。Stack剛創建后是空棧。
Set接口
Set是一種不包含重復的元素的Collection,即任意的兩個元素e1和e2都有e1.equals(e2)=false,Set最多有一個null元素。
很明顯,Set的構造函數有一個約束條件,傳入的Collection參數不能包含重復的元素。
請注意:必須小心操作可變對象(Mutable Object)。如果一個Set中的可變元素改變了自身狀態導致Object.equals(Object)=true將導致一些問題。
Map接口
請注意,Map沒有繼承Collection接口,Map提供key到value的映射。一個Map中不能包含相同的key,每個key只能映射一個 value。Map接口提供3種集合的視圖,Map的內容可以被當作一組key集合,一組value集合,或者一組key-value映射。
Hashtable類
Hashtable繼承Map接口,實現一個key-value映射的哈希表。任何非空(non-null)的對象都可作為key或者value。
添加數據使用put(key, value),取出數據使用get(key),這兩個基本操作的時間開銷為常數。
Hashtable通過initial capacity和load factor兩個參數調整性能。通常缺省的load factor 0.75較好地實現了時間和空間的均衡。增大load factor可以節省空間但相應的查找時間將增大,這會影響像get和put這樣的操作。
使用Hashtable的簡單示例如下,將1,2,3放到Hashtable中,他們的key分別是”one”,”two”,”three”:
Hashtable numbers = new Hashtable();
numbers.put(“one”, new Integer(1));
numbers.put(“two”, new Integer(2));
numbers.put(“three”, new Integer(3));
要取出一個數,比如2,用相應的key:
Integer n = (Integer)numbers.get(“two”);
System.out.println(“two = ” + n);
由于作為key的對象將通過計算其散列函數來確定與之對應的value的位置,因此任何作為key的對象都必須實現hashCode和equals方法。hashCode和equals方法繼承自根類Object,如果你用自定義的類當作key的話,要相當小心,按照散列函數的定義,如果兩個對象相同,即obj1.equals(obj2)=true,則它們的hashCode必須相同,但如果兩個對象不同,則它們的hashCode不一定不同,如果兩個不同對象的hashCode相同,這種現象稱為沖突,沖突會導致操作哈希表的時間開銷增大,所以盡量定義好的hashCode()方法,能加快哈希表的操作。
如果相同的對象有不同的hashCode,對哈希表的操作會出現意想不到的結果(期待的get方法返回null),要避免這種問題,只需要牢記一條:要同時復寫equals方法和hashCode方法,而不要只寫其中一個。
Hashtable是同步的。
HashMap類
HashMap和Hashtable類似,不同之處在于HashMap是非同步的,并且允許null,即null value和null key。,但是將HashMap視為Collection時(values()方法可返回Collection),其迭代子操作時間開銷和HashMap 的容量成比例。因此,如果迭代操作的性能相當重要的話,不要將HashMap的初始化容量設得過高,或者load factor過低。
WeakHashMap類
WeakHashMap是一種改進的HashMap,它對key實行“弱引用”,如果一個key不再被外部所引用,那么該key可以被GC回收。
總結
如果涉及到堆棧,隊列等操作,應該考慮用List,對于需要快速插入,刪除元素,應該使用LinkedList,如果需要快速隨機訪問元素,應該使用ArrayList。
如果程序在單線程環境中,或者訪問僅僅在一個線程中進行,考慮非同步的類,其效率較高,如果多個線程可能同時操作一個類,應該使用同步的類。
要特別注意對哈希表的操作,作為key的對象要正確復寫equals和hashCode方法。
盡量返回接口而非實際的類型,如返回List而非ArrayList,這樣如果以后需要將ArrayList換成LinkedList時,客戶端代碼不用改變。這就是針對抽象編程。
http://taoliyi.maifou.net/
http://www.kulego.com/shopex/
http://club.joyes.com/assort-2.html
摘要: 讀書筆記——《Ant – The Definitive Guide,2nd Edition》
????
選擇自 hopson 的 Blog
關鍵字
...
閱讀全文
???作者:江南白衣
?? 這篇文檔是專門寫給那些編程狂熱者,在Ant里編程時要留意的重要Task。
??? 不知為何,老外的各種腳本都寫得格外漂亮。從Appfuse里學到很多,在編寫SpringSide2.0
的構件安裝腳本時又被迫自學了不少,這里作下總結。
??? 如果只說一樣最重要的事情,就是ant-contrib
的<if> 和 <for>節點,使Ant 擁有了完整的編程能力。
1. 變量
?? Ant里的變量有個詭異的特性----一旦被賦值就不會改變,這個特性有時候幫助很大,有時候讓人很苦惱,一定要注意。另一樣要注意的是,Ant里的變量和其他語言的變量一樣,有可效范圍。
?? 1.由命令行賦值
????? ant build.xml -Dtomcat.home=foo
?? 2.與用戶交互輸入--Input task
????? <input message="請選擇一個Target "
?????????????? validargs="compile,jar,test"
?????????????? addproperty="my.input"/>
?3.從propertis文件讀取并存盤 --
propertyfile task
????
????? <propertyfile file="my.properties">
??????????????<entry key="springside.home" default="."/>
??????</propertyfile>
????? 如果my.properties 不存在,生成my.properties文件,springside.home=.。有一個特別有用的地方:有些properties文件的屬性每個開發者都不同,不想放入svn,但又想初始化數值,可以用該命令。
???? <propertyfile file="my.properties">
??????????????<entry key="springside.home" value="....."/>
??????</propertyfile>
????? 重新寫入配置文件。
2. 流程控制
???? 如果沒有ant-contrib 貢獻的<if> 和<for>節點,Ant的可編程性是極低極低的。
?2.1 if task
?ant原來可以在target級進行if判斷(unless,if 屬性),但實在太不方便了。
2.2 Conditions
但Ant預先封裝的一堆condition很是很方便的。這些condition完全從實際出發,包括文件是否存在,http://localhost:8080
是否連通都可以作為條件,見Ant的參考手冊
。
2.3 For task
支持"a,b,c,d" 字符串數組的循環與文件目錄,Fileset的循環。
2.4 Parallel task
Parallel非常有用,比如我想一邊開tomcat,一邊做別的,就需要使用它,否則就只有用spawn=true屬性把tomcat放在后臺運行。spawn有很多不好的地方,比如不能即時在console看到信息,停止ant運行不能把tomcat關掉等。
Parallel相當于一個容器,放在里面的每個task都會被并行執行。如果想把某幾個task順序執行,用相當于()的Sequential task
?包起來。
2.5 Waitfor task
暫停ant執行直到條件符合,比如<waitfor><http url=http://localhost:8080/
></waitfor>就會等待tomcat啟動后才會繼續往下執行。Macrodef task
3. 代碼封裝
?ant 代碼最基本的封裝是
?1. ant? task:調用其他腳本的任務,可設定dir 與是否繼承本腳本的變量。
?2. antcall task:調用本腳本內其他task,可設置參數。
?3. import task :就像其他語言的include一樣,引入其他腳本內容到本腳本里。
1. AntFetch
,? AntCallBack task
?? ant-contrib貢獻,對應于Ant 與 AntCall。原版只能向被調用函數傳遞變量,函數執行后沒辦法return 值。antcallback的語法如下
???<antcallback target="mytarget" return="myresult1,myresult2"/>
2. Macrodef task
???作為最小的封裝單位,與以<target>封裝再<antcall target="xxx">調用差不太遠,細微之處自行體驗了。個人比較喜歡用macrodef。
3. Java task
與 Exec task
直接執行Java類或程序
???注意執行目錄的定義。另在Windows下如果要直接運行dos窗口中的命令,以下指令啟動默認瀏覽器訪問localhost:
?? <exec executable="cmd.exe">
????? <arg line="/c start http://localhost:8080
"/>
???</exec>???
4. 擴展Ant的Task
?? 擴展ant task很簡單,實現execute() 方法執行task,實現setter接口讓ant框架執行屬性注入。繼承Task 獲得一些ant的能力,比如查詢某個變量的值。
?? 稍微有點麻煩的是多層嵌套屬性的注入。詳細請看http://ant.apache.org/manual/developlist.html
?? SpringSide 2.0 里很簡單的實現了一個XML File Merge的task,見XmlMergeTask.java。
???蛋蛋?說擴展Ant的最方便的方法還是在ANT里嵌套腳本。導入BSF庫以后,你就可以用BSF支持的腳本語言了(見Script Task)。接下來有機會嘗試一下。
?5. 文件操作
? 剛好springside里進行了比較多的文件操作,隨便記一下。
? replace
與 copy 時加入filter
, 都可以進行字符串替換.
??concat
在文件末添加其他文件的內容。
? 好困,很多東西沒寫詳細,明天再補充。
??
題外話,Ant
完整演示了如何編寫XML式的代碼,雖然對于開發人員來說XML編碼非常麻煩,遠遠沒有Ruby的rake以ruby代碼本身來構建系統清晰,但對于
IDE,特別是希望圖形化編程的IDE來說,XML比普通代碼要容易渲染得多,所以普元EOS的圖形化編程也是序列成XML代碼。再另外,Ant的
task 和 普元的構件也有相似。
?