java数组和集合
一、集合的概念
集合是源于數(shù)學(xué)中的術(shù)語,集合的一些原理和算法來自于數(shù)學(xué)中的理論。在java中,集合類是用來存放對象的。對于集合的使用是通過實例化集合類得到集合對 象。而集合對象則代表以某種方式組合到一起的一組對象,對于這組對象的使用是通過引用集合對象來進行的。
通過班級的例子來給集合舉例:(示意代碼如下)
班級集合 班級A = new 班級集合()// 班級A代表班級對象引用
// 在班級里添加學(xué)生
班級A.加入學(xué)生方法(學(xué)生對象A) ;
班級A.加入學(xué)生方法(學(xué)生對象B) ;
// 使用集合內(nèi)對象
學(xué)生 學(xué)生對象1 = (學(xué)生)班級A.取得一個對象();
學(xué)生對象1 . 學(xué)生對象自己的方法();
。。。。。
。。and so on。。。
通過例子,我們看看是否有共性的部分可以提取出來呢?經(jīng)分析,我們發(fā)現(xiàn):
(1)、集合的操作都是相似的,向集合內(nèi)添加元素(存儲),從集合中取得元素(檢索),從集合中刪除元素,修改集合的某個元素(注意,不是修改元 素本身屬性),還有其他操作(元素排序等)
(2)、集合都是用來存儲對象的(集合內(nèi)存儲的每個對象我們稱為元素或者元素對象)
(3)、集合要有一定的容量,能過存放多個元素。
數(shù)組就可以實現(xiàn)上面的需求,但java里面還有一些重要的API來實現(xiàn)集合類也能實現(xiàn),這時候,你就會問了,既然數(shù)組能實現(xiàn)容納對象的容器功能, 為什么還要設(shè)計這些集合類呢?
下面就看看集合和數(shù)組的區(qū)別:(寫一個程序例子)
import java.util.ArrayList;
public class StudentDemo {
//學(xué)生類
private String name;
private int age;
public StudentDemo(String name, int age){
this.name = name;
this.age = age;
}
public static void main(String[] args) {
// TODO Auto-generated method stub
//先寫一個數(shù)組的例子
StudentDemo[] sArray = new StudentDemo[3];
sArray[0]=new StudentDemo("jack",20);
sArray[1]=new StudentDemo("marry",18);
sArray[2]=new StudentDemo("terry",22);
StudentDemo stu1 = sArray[0];
System.out.println("this is array="+stu1.name+"?? this age="+stu1.age);
//在寫一個集合的例子
ArrayList coll = new ArrayList();
coll.add(new StudentDemo("marryone",18));
coll.add(new StudentDemo("jackone",22));
coll.add(new StudentDemo("terryone",23));
StudentDemo stu2 = (StudentDemo)coll.get(0);
System.out.println("this is coll= "+stu2.name+"??? this is age"+stu2.age);
}
}
從例子中來比較一下數(shù)組和集合的區(qū)別:
(1).數(shù)組的長度是固定的,在定義數(shù)組的時候,就要確定數(shù)組長度大小。而集合類在定義的時候不需要確認(rèn)集合所能容納對象的數(shù)量,也就是說集合的 大小是動態(tài)調(diào)整的。在實際應(yīng)用中我們不能確認(rèn)的需要存放元素數(shù)量的時候,采用集合是比較好的。容量可變是集合相對于數(shù)組的最大區(qū)別之一。
(2)數(shù)組既可以容納基本數(shù)據(jù)類型,也可以容納對象。而集合只能容納對象,不容納基本數(shù)據(jù)類型。另外,需要強調(diào)一點的是,無論是數(shù)組還是容器,當(dāng)其容納對 象的時候,存放的都是對象的引用。而數(shù)組在容納基本數(shù)據(jù)類型的時候,持有的則是數(shù)值。
(3) 如果想在集合中容納基本數(shù)據(jù)類型,該怎么做呢?
當(dāng)我們把一個對象放到集合中,該對象好像“失去”了它的型別特征,以后在使用的時候,除非使用者知道存入對象的類型,否則無法正確使用該對象。所以要像上 面的例子一樣,要進行一次強制類型的轉(zhuǎn)化
StudentDemo stu2 = (StudentDemo)coll.get(0);將其轉(zhuǎn)化為原來存儲時的對象。
二,在談?wù)剶?shù)組
在數(shù)組的處理上,除了數(shù)組本身提供的特性外,java還實現(xiàn)了一個工具類Arrays,提供了部分公用的static方法增強數(shù)組的處理功能。
介紹下Arrays工具類的公用方法
fill()??? -------???? 將某個數(shù)值填入到數(shù)組內(nèi)
sort()? -------??? 對數(shù)組內(nèi)的元素進行排序
binarySearch() ------? 在已排序的數(shù)組中查找
asList() ------?? 把數(shù)組轉(zhuǎn)換為List
等等還有一些其他的方法,這里就先介紹下sort()和binarySearch()的用法吧,一樣也是要用例子來說明的:
1,先說說sort()吧,排序需要對數(shù)組內(nèi)元素進行比較,基本數(shù)據(jù)類型(比如數(shù)字、字符串)等是比較簡單的。
但是,對于對象的比較,還是需要進行一些額外的處理的。比如,對于一份學(xué)生的考試成績單來說,成績單包含的信息如學(xué)生的學(xué)號,姓名,成績等。我們 要排序的時候,就要確定要按照成績單的那個屬性進行排序的。
對于對象的比較,有兩種方法來實現(xiàn)。
第一種方法:欲比較的類實現(xiàn)Comparable接口,該類就具有了比較的能力。而實現(xiàn)該類接口的時候,只有一個方法compareTo() 需要實現(xiàn),該方法接受另外一個對象作為比較參數(shù),兩個對象相等時返回0,該對象小于參數(shù)對象的時候,就返回正值,大于返回負(fù)值。類實現(xiàn) Comparable接口后,可以調(diào)用Arrays.sort()方法對數(shù)組內(nèi)的類對象進行排序。
如:
import java.util.Arrays;
import java.util.Collections;
public class TestResult implements Comparable {
int point;
int no;
String name;
public TestResult(int point,int no,String name){
this.point = point;
this.no = no;
this.name = name;
}
public int compareTo(Object o) {
int point_cp = ((TestResult)o).point;
return (point < point_cp ? -1:(point == point_cp ? 0:1));
}
public static void main(String[] args) {
TestResult[] tra = { new TestResult(90,2,"ahang"),new??????????????????????? TestResult(80,3,"wang"),new TestResult(100,1,"zhao")};
Arrays.sort(tra);
/*使用Arrays.sort()默認(rèn)的排序是升序的,那我們想要降序排列的話就使用下面的一句話就可以了*/
//Arrays.sort(tra,Collections.reverseOrder());
for(int i = 0, trsleng = tra.length; i<trsleng;i++){
System.out.println(tra[i].name + ","+tra[i].point);
}
}
}
執(zhí)行結(jié)果如下:
wang,80
ahang,90
zhao,100
第二種方法:如果我們拿到一個類,但是這個類沒有實現(xiàn)Comparable接口,或者我們不想使用該類默認(rèn)實現(xiàn)的Comparable接口的比較 方法,我們想采用自己的思路來排序。變通的做法就是新創(chuàng)建一個比較算法類,該類實現(xiàn)的是Comparator接口(不是Comparable接口了),實 現(xiàn)的時候,比較方法參數(shù)的2個對象。該比較類不是具體的比較參與對象,知識算法提供者(comparator),在需要使用排序的時候,該算法作為參數(shù)傳 遞給排序方法。(紅色字部分說的不是很簡單命了,其實我理解就是,自己寫了一個排序的方法,然后把要排序的對象做為參數(shù)傳遞進去,排序后在有該方法返回一 個排序完的結(jié)果)
例子如下:
import java.util.Arrays;
import java.util.Comparator;
/**
* 功能:由自己創(chuàng)建一個類并實現(xiàn)Comparator接口,來完成數(shù)組對對象的排序
* 作者:jackrui
* 時間:2009.3.27
*/
class ComResult implements Comparator{
public int compare(Object o1, Object o2) {
int no1 = ((TestResultComparator)o1).no;
int no2 = ((TestResultComparator)o2).no;
return (no1<no2?-1:(no1 == no2 ?0:1));
}
}
public class TestResultComparator implements Comparable{
int point;
int no;
String name;
public TestResultComparator(int point,int no,String name){
this.point = point;
this.no = no;
this.name = name;
}
public int compareTo(Object o) {
int point_cp = ((TestResultComparator)o).point;
return (point<point_cp ?-1:(point == point_cp?0:1));
}
public static void main(String[] args) {
TestResultComparator[] ra = {
new TestResultComparator(90,1,"marry"),
new TestResultComparator(100,2,"jackRui"),
new TestResultComparator(80,3,"Andy"),???????
};
//為了比較出區(qū)別,所以這里把2個實現(xiàn)比較的方法接口都實現(xiàn)了,如果用到下面這個 就可以不用實現(xiàn)Comparable接口
Arrays.sort(ra, new ComResult());
//下面的的注釋就是要實現(xiàn)Comparable接口才能實現(xiàn)比較
//Arrays.sort(ra);
for(int i = 0,raLength = ra.length; i < raLength;i++){
System.out.println(ra[i].name+ ","+ra[i].no);
}
}
}
2、在數(shù)組中進行查找
如何查找元素,我們在數(shù)據(jù)結(jié)構(gòu)中已經(jīng)學(xué)到了很多的算法,當(dāng)很多算法強調(diào)的重要的一點是:數(shù)據(jù)要先排序,后查找。排序后的數(shù)組可以使用一些快速的查 詢算法,效率會更高。
Arrays.binarySearch() 就是這這種策略的一個實現(xiàn),在數(shù)據(jù)結(jié)構(gòu)和算法中,該方法稱為2分法。
現(xiàn)在改造一下上面的例子,變成如下的代碼
import java.util.Arrays;
import java.util.Comparator;
/**
* 功能:由自己創(chuàng)建一個類并實現(xiàn)Comparator接口,來完成數(shù)組對對象的排序
* 作者:jackrui
* 時間:2009.3.27
*/
class ComResult implements Comparator{
public int compare(Object o1, Object o2) {
int no1 = ((TestResultComparator)o1).no;
int no2 = ((TestResultComparator)o2).no;
return (no1<no2?-1:(no1 == no2 ?0:1));
}
}
public class TestResultComparator{
int point;
int no;
String name;
public TestResultComparator(int point,int no,String name){
this.point = point;
this.no = no;
this.name = name;
}
public static void main(String[] args) {
ComResult cm =? new ComResult();
TestResultComparator marry =
new TestResultComparator(90,3,"marry");
TestResultComparator jackRui =
new TestResultComparator(100,2,"jackRui");
TestResultComparator Andy =
new TestResultComparator(80,1,"Andy");
TestResultComparator billy =
new TestResultComparator(60,4,"billy");
TestResultComparator[] ra = {marry,jackRui,Andy};
Arrays.sort(ra,cm);
//Arrays.sort(ra);
//排序后進行查找
/*此處排序后的查找要注意的是,當(dāng)使用第一種寫法的時候,即實現(xiàn)comparable來進行排序的時候,下面的 Arrays.binarySearch(ra, jackRui,cm)可以不用寫cm,如果要是采用第2種寫法的時候,一定要寫這個,不然的話,會報出錯的信息*/
int index1 = Arrays.binarySearch(ra, jackRui,cm);
int index2 = Arrays.binarySearch(ra, marry,cm);
int index3 = Arrays.binarySearch(ra, Andy,cm);
int index4 = Arrays.binarySearch(ra, billy,cm);
for(int i = 0,raLength = ra.length; i < raLength;i++){
System.out.println(ra[i].name+ ","+ra[i].no);
}
System.out.println();
System.out.println();
System.out.println(index1);
System.out.println(index2);
System.out.println(index3);
System.out.println(index4);
}??
}
執(zhí)行結(jié)果如下:
Andy,1
jackRui,2
marry,3
1
2
0
-4
解析:輸出的結(jié)果大于等于0 表示查找到該元素,并且返回元素在數(shù)組中的索引值。
輸出的結(jié)果小于0,則表示沒有找到該元素,負(fù)值的大小表示如果該元素要插入到數(shù)組中的索引號。
需要注意的是:如果排序采用的是第2種方法,也就是單獨寫排序算法的類提供comparator,則在查找的時候,也要采用該comparator。就像 我寫的這個例子一樣。
java2 集合框架層次結(jié)構(gòu)
1.1 java2集合
java2 的集合框架除了提供可變?nèi)萘康摹叭萜鳌碧匦酝?#xff0c;還豐富了一些其他數(shù)據(jù)結(jié)構(gòu)的實現(xiàn),比如鏈表(linked-list),隊列(queue)
java2集合主要包含2大類:
A、 collection:collection內(nèi)包含著一組單獨的元素,而根據(jù)元素存放規(guī)則又分為
(1)、List:以線性方式存儲,元素存儲有特定順序,元素可以重復(fù)。
(2)、Set:集合內(nèi)元素不可以重復(fù),Set內(nèi)元素是無序的。
B、Map:一群以Key/value(鍵/值對)構(gòu)成的成對對象集合,集合內(nèi)的每個元素都包含2個對象--也就是成對對象。這種存儲模式類似于數(shù)據(jù)庫中 一條記錄的存儲,該記錄包含主鍵(key字段)和其他字段(組成value值),當(dāng)我們想查一條記錄的時候,可以非??焖俚囊罁?jù)主鍵數(shù)值查詢出該記錄。從 map的key值快速的查詢出value值,這是map的主要應(yīng)用。key對象在map中是不可以重復(fù)的,換個角度來理解,所有的key兌現(xiàn)的合集就是一 個Set。
如上圖所示:
最原始的只有2中集合接口,Collection和Map 。Collection接口又有2個子接口List和Set,而在最終實現(xiàn)上,沒有直接基于Collection接口的實現(xiàn),List、Set和Map都 有相應(yīng)集合的實現(xiàn)類。(實際上,也是間接的實現(xiàn),中間還有層抽象類)
a、List接口實現(xiàn)包括ArrayList和LinkedList,還有傳統(tǒng)的java1.1中實現(xiàn)的Vector,與Vector相關(guān)的還有一個 Stack。ArrayList是可變?nèi)萘繑?shù)組,其特性和數(shù)組很接近;LinkedList是數(shù)據(jù)結(jié)構(gòu)中的鏈表,鏈表的最大特性就是對元素的刪除和添加操 作效率非常高;Vector是傳統(tǒng)的集合實現(xiàn),已經(jīng)不在推薦使用了,其實現(xiàn)完全可以用ArrayList和LinkedList代替。Stack是數(shù)據(jù)結(jié) 構(gòu)中棧的java實現(xiàn),是一種后進現(xiàn)出的數(shù)據(jù)結(jié)構(gòu),因為其采用的是Vector來實現(xiàn),所以也不被推薦使用。
b、Set接口的實現(xiàn)包括HashSet和TreeSet。HashSet是數(shù)據(jù)散列,而TreeSet是一種排序的Set。
c、Map接口的實現(xiàn)包括HashMap、TreeMap和HashTable。HashMap是最普通的Map實現(xiàn),Map內(nèi)元素成散列存取。 TreeMap是排序??????????????????????? Map。HashTable是傳統(tǒng)的java實現(xiàn),現(xiàn)在已經(jīng)不推薦使用了,已經(jīng)被HashMap代替。
d、除了集合的四種接口Collection、Map、List、Set及其相關(guān)實現(xiàn)類外,集合框架中還有與其(collection分支)關(guān)系密切的一 種特殊?????????????????? 接口Iterator(迭代器),而Iterator又有一個子接口實現(xiàn)ListIterator。Iterator對象是由Collection接口對 象產(chǎn)生的,而ListIterator則是由???????????????? List接口對象產(chǎn)生的。Collection接口對象包含Collection子接口的所有實現(xiàn)(List和Set接口的所有實現(xiàn)),也就是 說,Collection所?????????????????? 有子接口對象都可以產(chǎn)生Iterator對象。List接口的實現(xiàn)類除了可以產(chǎn)生?? Iterator 對象外,還可以產(chǎn)生ListIterator對象,那么Iterator有
什么用呢?請看下一小節(jié)。
1.2 小談一下Iterator(迭代器)
集合是存放一組元素的,元素的存入和取出必然需要集合的實現(xiàn)類提供方法來支持。而不同的實現(xiàn)類提供的方法可能不同。如果開始設(shè)計的時候我們選擇的集合是 ArrayList,后來我們發(fā)現(xiàn)采用LinkedList更合理。但我們要改變實現(xiàn)的時候,所有元素訪問的代碼(原ArrayList訪問方法)都要修 改成新的設(shè)計中的元素訪問方法(新的LinkedList訪問方法)。如果能有這樣一種思路,把“集合內(nèi)元素的訪問”和“實現(xiàn)采用的集合”分離開。把設(shè)計 中可變因素和不可變因素分離開。這樣一種設(shè)計思想必然會降低以后程序修改和維護的代價。Iterator就是這種設(shè)計思想的體現(xiàn)。從集合的實現(xiàn)中取得 Iterator接口對象,然后通過Iterator對象遍歷集合,然后采用Iterator接口的通用方法在集合中訪問每一個元素,對于取得的元素執(zhí)行 相應(yīng)的動作,這種通用的對于集合遍歷的實現(xiàn)就是Iterator接口。
因為其通用性的原因,Iterator的功能上是比較簡單的,使用也有一些限制。比如,Iterator只能單項移動,Iterator接口提供的通用功 能如下:
(1)、從集合中獲取Iterator接口對象方法:集合對象.Iterator(),返回集合對象的Iterator接口對象。
(2)、Iterator對象.next():從集合中取得下一個元素,返回object類型。
(3)、Iterator對象.hasnext():檢查集合中是否還有下一個元素,返回布爾值。
(4)、Iterator對象.remove():從集合中刪除最近獲得對象元素,返回刪除的對象。
Iterator是為遍歷而設(shè)計的,能夠從集合中取出元素和刪除元素,但是沒有添加元素的功能。集合遍歷元素的順序就是集合中元素存儲的順序。
下面就來看看Iterator一個比較普遍的使用例子:
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
/**
* 功能:Iterator一個普通的小例子
* @author JackRui
* @date: 2009.03.31
*/
public class TestResultIter {
int point;
int no;
String name;
public TestResultIter(int point,int no,String name){
this.point = point;
this.no = no;
this.name = name;
}
public static void main(String[] args) {
List coll = new ArrayList();
TestResultIter jack = new TestResultIter(100,1,"jackrui");
TestResultIter mary = new TestResultIter(90,2,"mary");
TestResultIter terry = new TestResultIter(80,3,"terry");
coll.add(jack);
coll.add(mary);
coll.add(terry);
//定義一個迭代器,并指向裝載數(shù)據(jù)的集合
Iterator iter = coll.iterator();
while(iter.hasNext()){
System.out.println(((TestResultIter)iter.next()).name);
}
}
}
這里值得一說的是,集合中的元素在放入的時候會“失去”類型,所以在取出的時候,要進行強制類型轉(zhuǎn)化為當(dāng)初的類型。
小記:在集合框架的體系結(jié)構(gòu)中,還有一套公用工具類(Arrays和Collections)和2個比較接口(Comparable和 Comparator),我們對集合或數(shù)組的元素進行排序的時候可以使用工具類中已經(jīng)提供的方法(如,Arrays.sort()),對于比較的元素,要 實現(xiàn)Comparable接口,或者單獨設(shè)計一個比較算法類,該類實現(xiàn)Comparator接口。
1.3 Collection
Collection接口是Set接口和List接口的超類,所以Collection的特性都適用于Set和List接口,而Set和List 接口在Collection的基礎(chǔ)上。增加了新的特性和方法。下面是Collection接口的通用方法:
Boolean add(Object) 集合中加入一個對象,成功時返回true
Boolean addAll(Collection) 集合中加入另外一個集合對象
Int size() 集合內(nèi)容納元素的數(shù)量
Boolean isEmpty() 集合是否為空
Boolean contain(Object) 集合內(nèi)是否有參數(shù)對象
Iterator iterator() 產(chǎn)生一個迭代器
Object[] toArray() 返回一個包含所有元素的對象數(shù)組
Object[] toArray(Object[]) 把所有元素放入對象數(shù)組中
Boolean remove(Object) 從集合中刪除對象
Boolean removeAll(Collection) 清空指定集合
Boolean contaunsAll(Collection) 判斷集合內(nèi)是否包含子集
Boolean retainAll(Collection) 刪除子集合不包含的元素
Void clear() 清空集合
Collection中雖然提供了一些刪除的方法,如remove、removeall等,但是沒有提供get()取出元素的方法,因 為,Collection包括了可以按照順序取的List接口外,還包含了無序的Set接口,因為無序所以無法按照順序去取元素的get()操作。雖然沒 有實現(xiàn)get方法,但是對于
Collection的遍歷還是提供了通用的方法,推薦使用Iterator迭代器,而Iterator已經(jīng)提供了remove和get方法。
1.3.1? List
List 是有一定順序的集合(也稱為Sequence序列),它與Set的區(qū)別是List中的元素可以重復(fù),Set是無序的。
List對于Collection的主要增強功能有2點:
首先,因為List元素是有序性,所以每個元素都有一個Index值,與該索引值相關(guān)增加了一些方法:添加元素的時候,可以在指定集合位置添加元素。同 樣,刪除的時候也可以刪除指定位置的元素。當(dāng)然,也可以按照其索引值來取出元素。
其次,可以通過List取得ListIterator來訪問List,ListIterator接口是Iterator接口的子類,其增強的地方是 Iterator只能單項遍歷,而ListIterator可以是雙向的,另外,ListIterator有增加元素的功能,而Iterator沒有此功 能。
List增加的方法:
Void (int index,Object element) 在指定索引位置添加元素
Object get(int index) 在List指定位置添加元素對象
Int indexOf(Object o) 在List中查詢元素的索引值,如果元素不存在返回-1
Int lastIndexOf(Object o) List中如果存在多個重復(fù)的元素,則indexOf返回第一個匹配元素的index,lastIndexOf(Object o)返回最后一個匹配的元素的index
ListIterator ListIterator() 返回ListIterator迭代器
Object remove(int index) 刪除集合中指定位置的元素,并把該元素返回
Object set(int index,Object element) 重新設(shè)置集合中該位置的元素
ListIterator接口除了Iterator接口已經(jīng)具備的hasNext()、next()、remove()方法之外,還增加了下面的 一些主要方法:
ListIterator接口方法 方法說明
Void add(int index,Object element) 在指定的索引位置添加元素
Object get(int index) 在List指定位置取得元素對象
Void add(Object o) 向集合中添加元素
Boolean hasPrevious() 判斷集合中是否有前驅(qū)元素
Int nextIndex() 取得集合中下一個元素的index值
Object Previous() 取得集合中當(dāng)前位置的前驅(qū)元素
Int PreviousIndex() 取得集合中后繼元素的Index值
Void set(Object o) 更新集合當(dāng)前位置元素
1.3.1.1? ArrayList
ArrayList 集合的存取方式和數(shù)組操作很類似,可以按照index順序來存取集合中的元素,但是還是建議采用更通用的迭代器來進行ArrayList的遍歷。
ArrayList與數(shù)組最大的區(qū)別就是它是可變數(shù)組,在初始化ArrayList集合的時候,可以指定一個初始化容量(Capacity 集合中可容納元素的數(shù)量),不指定的時候,系統(tǒng)會指定一個默認(rèn)的容量值。當(dāng)我們向ArrayList集合添加元素的時候,實際上是存放元素數(shù)量 (size)在不斷的增加,當(dāng)容量不變,當(dāng)數(shù)量增長到初始容量大小的時候,因為沒有空間導(dǎo)致元素添加阻塞,這時候該集合的容量會按照一定增長策略自動增 長,容量增長后,可以繼續(xù)向集合中添加元素??勺償?shù)組是ArrayList的優(yōu)點,但從另外一個角度考慮,容量的增長是需要付出額外的代價的,所以在性能 上有所損失。性能問題的一種解決思路是我們可以在向集合添加大量元素之前,根據(jù)欲添加元素的數(shù)量,預(yù)先擴充容量,采用的是ensureCapacity方 法。
ArrayList是一種線性表,在內(nèi)存中是連續(xù)存儲的,適合于元素的隨機存取。添加和刪除操作是需要依據(jù)添加的位置來定,如果在ArrayList最后 元素后面添加和刪除元素,在性能方面還算好,但是如果是在ArrayList中間添加和刪除元素的話,代價就會很大。因為,ArrayList需要維護整 個集合元素的順序存儲,所以需要處理欲添加和刪除元素位置之后的所有元素。
ArrayList的實現(xiàn)不是線程安全的。也就是說,如果有多個線程同時操作ArrayList集合對象,而且其中至少有一個線程的操作涉及到集合對象中 元素的修改(添加和刪除),則該線程內(nèi)對集合對象操作的方法需要實現(xiàn)同步。這也是ArrayList與Vector的主要區(qū)別。在新的集合框架的實現(xiàn)上, 基于性能的考慮,大部分的集合設(shè)計都是線程不安全的。如果有同步需求,在用戶自己的實現(xiàn)中可以實現(xiàn)同步,實現(xiàn)的方法有2種:
1、在操作集合對象的方法上使用synchronized關(guān)鍵字。
2、如果方法不可修改,則可在定義集合的時候,定義同步化的集合對象(采用Collections工具類的方法),類似:
List list = Collection.synchronizedList(new ArrayList(.....));
(此處深入說明下,在采用Iterator遍歷集合的時候,如果有其他線程修改了集合(添加或刪除操作),那么Iterator的處理會中止并拋出 ConcurrentModificationException異常,這是集合處理中的Fail-safe特性)
ArrayList提供的方法中,除了Collection和List的公用方法外,又加入了一些新的方法。
ArrayList(int initialCapacity) 構(gòu)造器方法增加了集合初始化的最小容量
Void ensureCapacity(int minCapacity) 預(yù)擴充ArrayList的容量
Void trimToSize() 把集合的Capacity縮小到Size的大小
下面做一個ArrayList小例子:
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
/**
* @功能:ArrayList的應(yīng)用小例子
* @author JackRui
* @時間:2009.03.31
*/
public class ArrayListDemo {
public static void main(String[] args) {
//利用ArrayList本身的特性
System.out.println("利用ArrayList本身的特性");
ArrayList list = new ArrayList();
list.add("aaa");
list.add("bbb");
list.add("ccc");
list.remove("bbb");
list.add("ddd");
for (int i=0,lsize=list.size();i<lsize;i++){
System.out.println(list.get(i));
}
//利用Iterator遍歷
System.out.println("利用Iterator遍歷");
Collection clist = new ArrayList();
clist.addAll(list);//添加元素不能采用Iterator接口
Iterator iter = clist.iterator();
while(iter.hasNext()){
String s = (String)iter.next();
if(s.equals("ddd")){
iter.remove();//可以通過Iterator接口對元素進行刪除
}else{
System.out.println(s);
}
}
//利用ListIterator遍歷???????
System.out.println("利用ListIterator遍歷");
List list2 = new ArrayList();
ListIterator lit = list2.listIterator();
if(!lit.hasNext()){
lit.add("haha");//可以通過ListIterator接口進行集合元素的添加
lit.previous();
System.out.println(lit.next());
}
}
}
運行結(jié)果如下:
利用ArrayList本身的特性
aaa
ccc
ddd
利用Iterator遍歷
aaa
ccc
利用ListIterator遍歷
haha
解析:3種方法中,第一種方法不通用,不建議使用。第2種方法最通用,但僅支持單向遍歷,而且對象的添加需要分開實現(xiàn)。第3種方法可以雙向遍歷, 而且可以直接使用ListIterator接口來添加對象。
問題:為什么next取得元素后,可以直接System.ou.print呢?而在String s = (String)iter.next();的時候必須進行強制類型轉(zhuǎn)化呢?
1.3.1.2? LinkedList
linkedList 是功能最強大,使用最廣泛的java集合實現(xiàn)類。
linkedList是數(shù)據(jù)結(jié)構(gòu)中的鏈表的java實現(xiàn),元素在linkedList 中的存儲是一個元素接著一個元素串聯(lián)起來,通過每個元素維護其前驅(qū)或后繼節(jié)點的連接。linkedList的線性化特征已經(jīng)很弱了,因為它不需要順序存 儲,也正是這個原因,如果在需要隨機存取的場合是不太適用linkedList的,但是在添加和刪除元素的方面上,linkedList的性能還是很高 的。linkedList容納的元素的數(shù)量也是自動動態(tài)增長的。
linkedList最主要的功能方面的增強是在List的頭部或者尾部進行添加、刪除和取得元素,它直接提供了這些方法的實現(xiàn)。所以 linkedList可以非常方便的實現(xiàn)我們數(shù)據(jù)結(jié)構(gòu)中常見的Stack(棧)、queue(隊列)等。
linkedList增強功能的方法如下:
Void addFirst(Object o) 在List的頭部添加對象
Void addLast(Object o) 在List尾部添加對象
Object getFirst() 從List頭部取得對象
Object getLast() 從List尾部取得對象
Object removeFirst() 從List頭部刪除對象
Object removeLast() 從List尾部刪除對象
下面寫一個linkedList的例子:
這個例子是用LinkedList設(shè)計一個測試隊列,這也可以作為我們自己的類庫來使用。(隊列的特性是(FIFO)先進現(xiàn)出,要注意的是,當(dāng)取隊列的元 素的時候要判斷隊列是否為空,所以用到了isEmpty方法)
import java.util.LinkedList;
/**
* @功能:用LinkedList設(shè)計一個測試隊列
* @author JackRui
* @時間:2009.03.31
*
*/
public class QueueDemo {
private LinkedList list = new LinkedList();
public void put(Object o){
list.addFirst(o);
}
public Object get(){
return list.removeLast();
}
public boolean isEmpty(){
return list.isEmpty();
}
public static void main(String[] args) {
QueueDemo queue = new QueueDemo();
queue.put("one");
queue.put("two");
while(!queue.isEmpty()){
System.out.println(queue.get());
}
}
}
執(zhí)行結(jié)果如下:
one
two
解析:實現(xiàn)了隊列的特性 先進先出,先進的順序是one two 出來也是one? two
問題:如何將隊列改成棧?如何增加判斷隊列的長度方法?該實現(xiàn)中是否可以采用queue繼承LinkedList來設(shè)計呢?
比較:ArrayList和LinkedList的比較:ArrayList是順序存儲,而LinkedList是鏈?zhǔn)酱鎯?#xff1b;ArrayList適合隨機 查找的場合,按照索引值取元素、設(shè)置元素等操作性能高,但是插入和刪除元素的性能比較差,尤其是在集合中間做插入和刪除操作,而LinkedList插入 和刪除的性能高,不適合隨機查詢。
1.3.2? Set
Set接口是collection接口的另一個子接口。它與list的區(qū)別是Set集合內(nèi)存放的元素都是唯一的,不可以重復(fù)的。而且,向set里存放的元 素不是按照原來的順序存儲的。
set主要有2種集合實現(xiàn),一個是HashSet,一個是TreeSet,既然都屬于Set接口,所以,這兩種集合內(nèi)容納的對象也是不可以重復(fù)的。
HashSet 的特征在于其內(nèi)對象的散列存取,即采用哈希技術(shù),每個對象利用Hashcode()方法算出一個唯一的hash值,然后在根據(jù)該hash值把各個對象盡可 能均勻分布到集合中。當(dāng)讀取對象的時候,同樣先計算出對象的hash值,然后根據(jù)hash值快速到集合中相應(yīng)的位置(可以理解為hash值默認(rèn)的實現(xiàn)是與 存儲位置相關(guān)的數(shù)據(jù))取出對象元素。自然,hashSet的優(yōu)點在于快速定位元素。
HashSet內(nèi)存放的對象有2個要求,除了equals方法判斷元素時候重復(fù)外,還要使用hashCode()方法來快速定位元素,在需要的時候,這2 中方法都要覆蓋。
TreeSet雖然是存入的順序和存儲的順序是不一樣的,但是存儲是按照排序存儲的,也就是說在存儲的時候是用到了Arrays中提到的比較接口 Comparable和Comparator。
下面是一個HashSet和TreeSet的例子:
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import java.util.TreeSet;
/**
* @功能:實現(xiàn)一個HashSet和TreeSet的例子
* @author JackRui
* @時間:2009.03.31
*/
public class SetDemo {
private static void printCol(Collection coll){
Iterator iter = coll.iterator();
while(iter.hasNext()){
System.out.println(iter.next());
}
}
public static void main(String[] args) {
Collection coll = new ArrayList();
coll.add("c");
coll.add("a");
coll.add("b");
coll.add("a");
Set set1 = new HashSet();
Set set2 = new TreeSet();
set1.addAll(coll);
set2.addAll(coll);
System.out.println("現(xiàn)在開始打印HashSet的內(nèi)容: ");
printCol(set1);
System.out.println("現(xiàn)在開始打印TreeSet的內(nèi)容: ");
printCol(set2);
}
}
執(zhí)行結(jié)果如下:
現(xiàn)在開始打印HashSet的內(nèi)容:
a
c
b
現(xiàn)在開始打印TreeSet的內(nèi)容:
a
b
c
代碼解釋:從結(jié)果看到,無論是HashSet還是TreeSet,都沒有重復(fù)的值(像Set中重復(fù)存儲對象是可以執(zhí)行的,但只能存儲一次罷了,這 也是Set的基本特性。)? HashSet區(qū)別的地方是對象是散列開的,具體體現(xiàn)在輸出上。我們輸入的順序是cab,經(jīng)過HashSet存儲后輸出的順序變成acb了,而 TreeSet是有序的。
1.4? Map
Map是不同于Collection的另外一種集合接口,說到Map的適用范圍,我們先來看看一個很平常的選擇數(shù)據(jù)的例子。
我們在選擇數(shù)據(jù)的時候,我們選擇思路的依據(jù)是什么呢?比如,想從班級里選擇一個學(xué)生對象,班級中按照學(xué)生報道的先來后到順序排成學(xué)號,學(xué)號的同時代表了學(xué) 生的序號(班級里學(xué)生的索引值),學(xué)生還有姓名信息等,我們選擇的信息可能有這么幾種:
(1)、每次都取出班級最后來的學(xué)生-----棧的思路
(2)、每次都取出班級最先來的學(xué)生-----隊列的思路
(3)、按照每個學(xué)生的學(xué)號來查找學(xué)生----List的思路,如果學(xué)生的學(xué)號是連續(xù)的,則是ArrayList的思路,如果每個學(xué)生都是通過其他學(xué)生關(guān) 聯(lián)查詢出來的(比如按照座位次序),則是LinkedList思路。
(4)、如果班級里不允許重名的學(xué)生存在,這是set思路。
(5)、如果想通過學(xué)生的姓名查詢出該學(xué)生來,這就是map思路。
Map內(nèi)存儲的是鍵/值對這樣以成對的對象組(可以把一組對象當(dāng)成一個元素),通過“鍵”對象來查詢“值”對象,這個概念類似于數(shù)據(jù)庫中的表的查詢,表經(jīng) 常會設(shè)置主鍵,主鍵對于每條記錄來說都是唯一的,所以,只要能快速的找到主鍵,就可以快速的定位到一條記錄,從而取出這條記錄,這在數(shù)據(jù)庫中使用的方法是 索引。在Map中,鍵(key)值也是唯一的,我們通過一些方法快速的找到key,而key對象和value對象是關(guān)聯(lián)在一起的,自然也就找到了 value對象,從而快速取得value值。
Map接口有2個實現(xiàn),HashMap和TreeMap,HashMap中key/value對的存放和查詢是采用Hash算法,每個key對象都對應(yīng)唯 一的一個hash值,根據(jù)該hash值,可以快速的定位key對象,進行快速定位到value對象。在插入元素的時候,如果key已經(jīng)存在,則替換原來的 value對象。TreeMap中鍵/值對是排序(按照key排序)存儲的。
下面寫一個HashMap和TreeMap的小例子吧:
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Set;
import java.util.TreeMap;
/**
* @功能:實現(xiàn)一個HashMap和TreeMap的小例子
* @author JackRui
* @時間:2009.03.31
*
*/
public class MapDemo {
int point;
int no;
String name;
public MapDemo(int point,int no,String name){
this.point = point;
this.no = no;
this.name = name;
}
private static void printCol(Collection coll){
Iterator iter = coll.iterator();
while(iter.hasNext()){
System.out.println(iter.next());
}???????
}
public static void main(String[] args) {
HashMap map1 = new HashMap();
map1.put("Zrjack", new MapDemo(100,1,"ZrJackRui"));
map1.put("mary", new MapDemo(80,2,"mary"));
map1.put("terry", new MapDemo(90,3,"terry"));
MapDemo mapdemo = (MapDemo)map1.get("Zrjack");
System.out.println("jack's result is "+mapdemo.point);
Set set1 = map1.keySet();
printCol(set1);
TreeMap map2 = new TreeMap();
map2.put("Zrjack", new MapDemo(100,1,"ZrJackRui"));
map2.put("mary", new MapDemo(80,2,"mary"));
map2.put("terry", new MapDemo(90,3,"terry"));
MapDemo mapdemo1 = (MapDemo)map1.get("Zrjack");
System.out.println("jack's result is "+mapdemo1.point);
Set set2 = map2.keySet();
printCol(set2);
}
}
執(zhí)行結(jié)果:
jack's result is 100
terry
Zrjack
mary
jack's result is 100
Zrjack
mary
terry
結(jié)果分析:程序分別用了HashMap和TreeMap來存儲數(shù)據(jù),在TreeMap的時候,進行了排序(按首字母的大小進行排序),值得注意的地方是, 排序的時候,大寫字母永遠(yuǎn)都排在小寫字母前面,如A 要排在a的前面。
HashMap和HashTable? 的比較:HashTable是jiava傳統(tǒng)的實現(xiàn),其工作原理和功能和HashMap和接近,但是已經(jīng)不推薦使用了,HashTable是線程安全 的,HashMap不是線程安全的。從性能的角度上考慮,HashMap要比HashTable要好。
1.5 集合公用工具類&集合異常類
1.5.1 Collection 公用工具
Collection 類是類似于Arrays類的公用工具類,Arrays提供了供數(shù)組使用的公用方法,而Collection提供了一些static方法供集合類使用或操作 集合類。
Collection 中提供的方法比較多,這里說明比較常用的方法
Collection類的公用方法
Enumeration enumeration(Collection c) 返回傳統(tǒng)的一個遍歷接口Enumeration
Object max(Collection c) 返回集合中最大元素,需要考慮比較接口的實現(xiàn)
Object max(Collection c,Comparator comp) Max算法采用Comparator比較算法
Object min(Collection c) 返回集合中最小元素
Void reverse(Collection c) 把集合中的元素順序反轉(zhuǎn)
Void copy(List desc,List src) Src集合中的元素復(fù)制到desc集合
Void fill(List list, Object o) 填充list集合,填充的元素為o
Void sort(List list) 對一種list做排序
Int binarySearch(List list,Object key) 對排序后的集合list進行查詢元素操作
List synchronizedList(List list) 返回線程安全的List,同步方法還有Map,Set等的實現(xiàn)
在這些工具方法中,sort()、binarySearch()以及reverse()在排序和查找中比較常用,再次強調(diào)的是,要使用這些方法的時候,一 是集合中的元素要實現(xiàn)比較接口,二是一定要先比較,再查詢。
Enumeratruon 是java1中提供的遍歷集合的接口,已經(jīng)不在推薦使用了。但是在一些實現(xiàn)或者實際應(yīng)用中,可能還會看到Enumeratruon的使用,雖然我們推薦使 用Iterator接口,但是遇到Enumeratruon接口的時候,也要能理解,Enumeratruon提供的接口方法和Iterator很相似。
1.5.2 集合處理中的常見異常
集合處理中的常用異常表如下:
ClassCastException 從集合中取得元素對象在進行類型轉(zhuǎn)換的時候類型不匹配
UnsupportedOperationException 當(dāng)?shù)讓訉崿F(xiàn)沒有實現(xiàn)上層的相關(guān)方法的時候由collection拋出異常。Collection接口(或其他集合超類)中的一些函數(shù)在java doc中是標(biāo)明“可有可無(Optional)”的函數(shù),這些函數(shù)在底層的一些具體實現(xiàn)中,有的實現(xiàn)了,有的沒有實現(xiàn),當(dāng)我們調(diào)用底層實現(xiàn)集合類的這些有 實現(xiàn)的方法時就會拋出該異常。
ConcurrentModificationException 當(dāng)采用Iterator遍歷集合時,如果此集合中的元素被改變則Iterator遍歷拋出此異常
IndexOutOfBoundsException 集合中元素引用的索引值超出界限(<0或>size())
NoSuchElementException LinkedList中g(shù)etLast,getFirst等方法取元素的時候List為空。
以上異常都是RuntimeException的子類,在編程的時候可以不處理該異常,如果出現(xiàn)問題,在程序運行期間由jvm拋出。
下面寫一個關(guān)于ConcurrentModificationException異常的小例子
import java.util.Collection;
import java.util.Iterator;
import java.util.LinkedList;
/**
* @功能:一個關(guān)于ConcurrentModificationException異常的小例子
* @author JackRui
* @時間:2009.03.31
*/
public class CollExceptionDemo {
public static void main(String[] args) {
Collection coll = new LinkedList();
Iterator iter = coll.iterator();
coll.add("a");
String s = (String)iter.next();
}
}
執(zhí)行結(jié)果如下:
java.util.ConcurrentModificationException
。。。
1.5 .1 泛型
java1.5 中增加了泛型的概念,可以更方便的定義集合的數(shù)據(jù)類型和對集合中的數(shù)據(jù)進行訪問,關(guān)于泛型的概念需要單獨講解,就不在這里說了
轉(zhuǎn)載于:https://www.cnblogs.com/rafx/archive/2011/10/09/arraylist.html
總結(jié)
- 上一篇: 原来如此
- 下一篇: win7 IIS7.5 HTTP 错误