




版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)
文檔簡(jiǎn)介
Java語言程序設(shè)計(jì)第12章泛型與集合
1泛型23主要內(nèi)容Java語言程序設(shè)計(jì)(第4版)清華大學(xué)出版社2022集合框架List接口及實(shí)現(xiàn)類45Set接口及實(shí)現(xiàn)類Queue接口及實(shí)現(xiàn)類6案例:用集合存儲(chǔ)、遍歷員工記錄7Map接口及實(shí)現(xiàn)類89Collections類案例:用Map統(tǒng)計(jì)單詞數(shù)量Java語言程序設(shè)計(jì)12.1泛型泛型是類和接口的一種擴(kuò)展機(jī)制,主要實(shí)現(xiàn)參數(shù)化類型機(jī)制。使用泛型,程序員可以編寫更安全的程序。概述泛型是Java5引進(jìn)的新特征,泛型被廣泛應(yīng)用在Java集合API中,在Java集合框架中大多數(shù)的類和接口都是泛型類型?;仡櫼幌?,我們?cè)?.5節(jié)中定義了一個(gè)整數(shù)棧類IntStack,該類使用Integer作為棧的元素,這就限制了該類只能對(duì)Integer元素操作。12.1.1泛型類如果要使這個(gè)棧類更具有通用性,我們可以使用Object作為棧的元素,因?yàn)镺bject類是所有類的超類,所以O(shè)bject可以引用任何對(duì)象類型。然而,這種做法無法提供類型的安全性,在進(jìn)行類型轉(zhuǎn)換時(shí)可能發(fā)生類型不匹配異常。使用泛型就可以提高類型安全性,因?yàn)?,它可以使類型轉(zhuǎn)換自動(dòng)地、隱式地進(jìn)行。所謂泛型(generics)就是帶一個(gè)或多個(gè)類型參數(shù)(typeparameter)的類或接口。對(duì)于上述討論的對(duì)象棧,可以使用泛型定義。12.1.1泛型類為了簡(jiǎn)單,下面先定義一個(gè)泛型Node類表示節(jié)點(diǎn),類型參數(shù)T表示節(jié)點(diǎn)中存放的數(shù)據(jù)值類型。具體代碼如程序12.1所示。publicclassNode<T>{privateTdata;//泛型成員,T可以是任何引用類型publicNode(){}//默認(rèn)構(gòu)造方法publicNode(Tdata){//帶參數(shù)構(gòu)造方法this.data=data;}publicTget(){//訪問方法定義returndata;}程序12.1Node.javapublicvoidset(Tdata){//修改方法定義this.data=data;}//顯示類型名publicvoidshowType(){ System.out.println("T的類型是:"+data.getClass().getName());}}泛型類型的使用與方法調(diào)用類似,方法調(diào)用需向方法傳遞參數(shù),使用泛型需傳遞一個(gè)類型參數(shù),即用某個(gè)具體的類型替換T。例如,如果要在Node對(duì)象中存放Integer對(duì)象,就需要在創(chuàng)建Node對(duì)象時(shí)為其傳遞Integer類型參數(shù)。12.1.1泛型類要實(shí)例化泛型類對(duì)象,也使用new運(yùn)算符,但在類名后面需加上要傳遞的具體類型。Node<Integer>
intNode=newNode<Integer>();一旦創(chuàng)建了intNode對(duì)象,就可以調(diào)用set()方法設(shè)置其中的Integer對(duì)象,如下代碼所示。
publicstaticvoidmain(String[]args){Node<Integer>intNode=newNode<Integer>();intNode.set(Integer.valueOf(999));System.out.println(intNode.get());intNode.showType();}由于編譯器能夠從上下文中推斷出泛型參數(shù)的類型,所以從JavaSE7開始,在創(chuàng)建泛型類型時(shí)可使用菱形語法(diamond),即僅用一對(duì)尖括號(hào)(<>),上述創(chuàng)建intNode的語句可以寫成:Node<Integer>intNode=newNode<>();12.1.1泛型類按照約定,類型參數(shù)名使用單個(gè)大寫字母表示。常用的類型參數(shù)名有:E表示元素,K表示鍵,N表示數(shù)字,T表示類型,V表示值等。需要注意,泛型可能具有多個(gè)類型參數(shù),但在類或接口的聲明中,每個(gè)參數(shù)名必須是唯一的。與類一樣,接口也可以聲明為泛型接口,即接口也可以帶參數(shù),例如,下面的Readable接口使用泛型類型作為其read()方法的參數(shù):publicinterfaceReadble<T>{voidread(Tt);}12.1.2泛型接口類實(shí)現(xiàn)這個(gè)接口有三種方法。
第一個(gè)是指定類中的泛型類型。下面的具體類說它只處理Book。這使得它可以用一個(gè)Book參數(shù)聲明read()方法:publicclassIntelligentRobotimplementsReadable<Book>{publicvoidread(Bookb){}}12.1.2泛型接口下一種方法是創(chuàng)建一個(gè)泛型類。下面的具體類允許調(diào)用者指定泛型的類型:publicclassIntelligentRobot<U>implementsReadable<U>{publicvoidread(Ut){}}在本例中,類型參數(shù)可以被命名為任何名稱,包括T。我們?cè)诶又惺褂昧薝,這樣就不會(huì)混淆T指的是什么。12.1.2泛型接口最后一種方法是不使用泛型。這是編寫代碼的老方法。它產(chǎn)生一個(gè)編譯器警告Readable是一個(gè)原始類型,但它能夠編譯。這里的read()方法有一個(gè)Object參數(shù),因?yàn)榉盒皖愋蜎]有定義:publicclassIntelligentRobotimplementsReadable{publicvoidread(Objecto){}}12.1.2泛型接口泛型接口也可帶有多個(gè)類型參數(shù)。下面的Entry是泛型接口,帶兩個(gè)類型參數(shù)。12.1.2泛型接口publicinterfaceEntry<K,V>{publicKgetKey();publicVgetValue();}publicclassPair<K,V>implementsEntry<K,V>{privateKkey;privateVvalue;publicPair(Kkey,Vvalue){//構(gòu)造方法this.key=key;this.value=value;}程序12.2Pair.javapublicvoidsetKey(Kkey){this.key=key;}publicKgetKey(){returnkey;}publicvoidsetValue(Vvalue){this.value=value;}publicVgetValue(){returnvalue;}}下面語句創(chuàng)建兩個(gè)Pair類實(shí)例:Pair<String,Integer>p1=newPair<>("twenty-two",22);Pair<String,String>p2=newPair<>("china","Beijing");泛型方法(genericmethod)是帶類型參數(shù)的方法。類的成員方法和構(gòu)造方法都可以定義為泛型方法。泛型方法的定義與泛型類型的定義類似,但類型參數(shù)的作用域僅限于聲明的方法和構(gòu)造方法內(nèi)。泛型方法可以定義為靜態(tài)的和非靜態(tài)的。12.1.3泛型方法下面的MathUtils類中定義兩個(gè)static的泛型方法swap()和compare()。swap()方法用于交換任何數(shù)組中兩個(gè)元素(數(shù)組元素類型不是基本類型),compare()方法用于比較兩個(gè)泛型類Pair對(duì)象的參數(shù)K和V是否相等。特別注意,對(duì)于泛型方法必須在方法返回值前指定泛型,如<K,V>。publicclassMathUtils{publicstatic<T>voidswap(T[]array,inti,intj){Ttemp=array[i];array[i]=array[j];array[j]=temp;}publicstatic<K,V>booleancompare(Pair<K,V>p1,Pair<K,V>p2){returnp1.getKey().equals(p2.getKey())&&p1.getValue().equals(p2.getValue());}程序12.3MathUtils.javapublicstaticvoidmain(String[]args){Integer[]numbers={1,3,5,7};MathUtils.swap(numbers,0,3);for(Integern:numbers){System.out.println(n+"");//輸出:7351}Pair<Integer,String>p1=newPair<>(1,"apple");Pair<Integer,String>p2=newPair<>(2,"orange");//調(diào)用泛型方法booleansame=MathUpare(p1,p2);System.out.println(same);//輸出:false}泛型類型本身是一個(gè)Java類型,就像java.lang.String和java.time.LocalDate一樣,為泛型類型傳遞不同的類型參數(shù)會(huì)產(chǎn)生不同的類型。例如,下面list1和list2就是不同的類型對(duì)象。
List<Object>list1=newArrayList<Object>();List<String>list2=newArrayList<String>();12.1.4通配符(?)的使用這里L(fēng)ist和ArrayList是泛型接口和泛型類。盡管String是Object的子類,但List<String>與List<Object>卻沒有關(guān)系,List<String>并不是List<Object>的子類型。因此,把一個(gè)List<String>對(duì)象傳遞給一個(gè)需要List<Object>對(duì)象的方法,將會(huì)產(chǎn)生一個(gè)編譯錯(cuò)誤。請(qǐng)看下面代碼。12.1.4通配符(?)的使用publicstaticvoidprintList(List<Object>list){for(Objectelement:list){System.out.println(element);}}該方法的功能是打印傳遞給它的列表的所有元素。如果傳遞給該方法一個(gè)List<String>對(duì)象,將發(fā)生編譯錯(cuò)誤。如果要使上述方法可打印任何類型的列表,可將其參數(shù)類型修改為L(zhǎng)ist<?>,如下所示:publicstaticvoidprintList(List<?>list){for(Objectelement:list){System.out.println(element);}}這里,問號(hào)(?)就是通配符,它表示該方法可接受元素是任何類型的List對(duì)象。12.1.4通配符(?)的使用publicclassWildCardDemo{publicstaticvoidprintList(List<?>list){ for(Objectelement:list){ System.out.println(element); }}程序12.4WildCardDemo.javapublicstaticvoidmain(String[]args){ varmyList=newArrayList<String>(); myList.add("cat"); myList.add("dog"); myList.add("horse");printList(myList);}有時(shí)需要限制傳遞給類型參數(shù)的類型種類,例如,要求一個(gè)方法只接受Number類或其子類的實(shí)例,這就需要使用有界類型參數(shù)(boundedtypeparameter)。12.1.5有界類型參數(shù)有界類型分為上界和下界,上界用extends指定,下界用super指定。上界用extends指定,例如,List<?extendsNumber>,Number或Number的子類型下界用super指定,例如,List<?superInteger>,Integer或Integer的超類型publicstaticdoublegetAverage(List<?extendsNumber>numberList){ vartotal=0.0; for(varnumber:numberList){ total+=number.doubleValue(); } returntotal/numberList.size();}程序12.5BoundedTypeDemo.javapublicstaticvoidmain(String[]args){
varintegerList=newArrayList<Integer>(); integerList.add(3); integerList.add(30); integerList.add(300); System.out.println(getAverage(integerList));//輸出:111.0
vardoubleList=newArrayList<Double>(); doubleList.add(5.5); doubleList.add(55.5); System.out.println(getAverage(doubleList));//輸出:30.5當(dāng)實(shí)例化泛型類型時(shí),編譯器使用一種叫類型擦除(typeerasure)的技術(shù)轉(zhuǎn)換這些類型。在編譯時(shí),編譯器將清除類和方法中所有與類型參數(shù)有關(guān)的信息。類型擦除可讓使用泛型的Java應(yīng)用程序與之前不使用泛型類型的Java類庫和應(yīng)用程序兼容。2.1.6類型擦除例如,Node<Integer>被轉(zhuǎn)換成Node,它稱為源類型(rawtype)。源類型是不帶任何類型參數(shù)的泛型類或接口名。這說明在運(yùn)行時(shí)找不到泛型類使用的是什么類型。下面的操作是不可能的。publicclassMyClass<E>{publicstaticvoidmyMethod(Objectitem){if(iteminstanceofE){//編譯錯(cuò)誤…}
Eitem2=newE();//編譯錯(cuò)誤E[]iArray=newE[10];//編譯錯(cuò)誤Eobj=(E)newObject();//非檢查的造型警告}}Java語言程序設(shè)計(jì)12.2集合框架在編寫面向?qū)ο蟮某绦驎r(shí),經(jīng)常要用到一組類型相同的對(duì)象。可以使用數(shù)組來集中存放這些類型相同的對(duì)象,但數(shù)組一經(jīng)定義便不能改變大小。因此,Java提供了一個(gè)集合框架(collectionsframework),該框架定義了一組接口和類,使處理對(duì)象組更容易。集合框架集合是指集中存放一組對(duì)象的一個(gè)對(duì)象。集合相當(dāng)于一個(gè)容器。集合能夠幫助Java程序員輕松地管理對(duì)象。Java集合框架由兩種類型構(gòu)成,一個(gè)是Collection,另一個(gè)是Map。Collection對(duì)象用于存放一組對(duì)象,Map對(duì)象用于存放一組“關(guān)鍵字/值”的對(duì)象。Collection<E>接口是所有集合類型的根接口,它繼承了Iterable<E>接口,它有三個(gè)子接口:Set接口、List接口和Queue接口。Collection接口定義了集合操作的常用方法。可以簡(jiǎn)單分為以下幾類:基本操作、批量操作、數(shù)組操作和流操作。集合框架實(shí)現(xiàn)基本操作的方法添加元素、刪除指定元素、返回集合中元素的個(gè)數(shù)、返回集合的迭代器對(duì)象。1.基本操作booleanadd(Ee):向集合中添加元素e。booleanremove(Objecto):從集合中刪除指定的元素o。booleancontains(Objecto):返回集合中是否包含指定的元素o。booleanisEmpty():返回集合是否為空,即不包含元素。intsize():返回集合中包含的元素個(gè)數(shù)。Iteratoriterator():返回包含所有元素的迭代器對(duì)象。下面的方法可實(shí)現(xiàn)集合的批量操作。booleanaddAll(Collection<?extendsE>c):將集合c中的所有元素添加到當(dāng)前集合中。booleanremoveAll(Collection<?>c):從當(dāng)前集合中刪除c中的所有元素。defaultbooleanremoveIf(Predicate<?superE>filter)booleancontainsAll(Collection<?>c):返回當(dāng)前集合是否包含c中的所有元素。booleanretainAll(Collection<?>c):在當(dāng)前集合中只保留指定集合c中的的元素,其他元素刪除。
voidclear():將集合清空。
2.批量操作下面方法可以將集合元素轉(zhuǎn)換成數(shù)組元素。Object[]toArray():返回包含集合中所有元素的對(duì)象數(shù)組。
<T>T[]toArray(T[]a):返回包含集合中所有元素的數(shù)組,返回?cái)?shù)組的元素類型是指定的數(shù)組類型。3.數(shù)組操作設(shè)c是一個(gè)Collection對(duì)象,下面的代碼將c中的對(duì)象轉(zhuǎn)換成一個(gè)新的Object數(shù)組,數(shù)組的長(zhǎng)度與集合c中的元素個(gè)數(shù)相同。Object[]a=c.toArray();Java語言程序設(shè)計(jì)12.3List接口及實(shí)現(xiàn)類List接口是Collection的子接口,它實(shí)現(xiàn)一種線性表的數(shù)據(jù)結(jié)構(gòu)。存放在
List中的所有元素都有一個(gè)下標(biāo)(從0開始),可以通過下標(biāo)訪問List中的元素。List中可以包含重復(fù)元素。List接口的實(shí)現(xiàn)類包括ArrayList、LinkedList、Vector和Stack。概述北京上海廣州bigCities012List接口除繼承Collection的方法外,還定義了一些自己的方法。使用這些方法可以實(shí)現(xiàn)定位訪問、查找、迭代和返回子線性表。List的常用方法如下。12.3.1List的操作booleanadd(Ee):向集合中添加元素e。voidadd(intindex,Eelement):將指定元素插入到指定下標(biāo)處。Eget(intindex):返回指定下標(biāo)處的元素。Eset(intindex,Eelement):修改指定下標(biāo)處的元素。static
<E>
List<E>
of?(E...
elements):返回包含任意數(shù)量元素的不可修改列表。該方法還接受單個(gè)數(shù)組作為參數(shù)。結(jié)果列表的元素類型將是數(shù)組的元素類型,列表的大小等于數(shù)組的長(zhǎng)度。Eremove(intindex):刪除指定下標(biāo)處的元素。intindexOf(Objecto):查找指定對(duì)象第一次出現(xiàn)的位置。intlastIndexOf(Objecto):查找指定對(duì)象最后一次出現(xiàn)的位置。List<E>subList(intfrom,intto):返回從from到to元素的一個(gè)子線性表。12.3.1List的操作下面代碼使用List的of()方法創(chuàng)建一個(gè)不可修改的List對(duì)象,對(duì)不可修改的對(duì)象調(diào)用修改方法將拋出運(yùn)行時(shí)異常。List<String>nameList=List.of("趙","錢","孫","李");System.out.println(nameList);//輸出:[趙,錢,孫,李]//nameList.add("周");//nameList.set(1,"吳");//nameList.remove("李");System.out.println(nameList.indexOf("孫"));//輸出:2產(chǎn)生運(yùn)行時(shí)異常ArrayList類實(shí)際上實(shí)現(xiàn)了一個(gè)變長(zhǎng)的對(duì)象數(shù)組,其元素可以動(dòng)態(tài)地增加和刪除。它的定位訪問時(shí)間是常量時(shí)間。12.3.2ArrayList類ArrayList的構(gòu)造方法如下:publicArrayList()publicArrayList(Collectionc)publicArrayList(intinitialCapacity)下列代碼創(chuàng)建一個(gè)ArrayList對(duì)象并向其中插入幾個(gè)元素,并使用ArrayList的有關(guān)方法對(duì)它操作。varcities=newArrayList<String>();cities.add("北京");cities.add("上海");cities.add("廣州");System.out.println(cities.size());cities.add(1,"倫敦");cities.set(1,"紐約");System.out.println(cities.contains("北京"));System.out.println(cities);System.out.println(cities.indexOf("巴黎"));遍歷集合中的元素有多種方法:用簡(jiǎn)單的for循環(huán)、用增強(qiáng)的for循環(huán)和用Iterator迭代器對(duì)象。12.3.3遍歷集合元素1.使用for循環(huán)使用for循環(huán)可以遍歷集合中的每個(gè)元素。for(vari=0;i<cities.size();i++){System.out.print(cities.get(i)+"");}2.使用增強(qiáng)的for循環(huán)使用增強(qiáng)的for循環(huán)不但可以遍歷數(shù)組的每個(gè)元素,還可以遍歷集合中的每個(gè)元素。下面的代碼打印集合的每個(gè)元素:for(varcity:cities){System.out.println(city);}12.3.3遍歷集合元素上述代碼的含義是:依次將集合cities中的每個(gè)對(duì)象存儲(chǔ)到city變量中,然后打印輸出。如果只是簡(jiǎn)單輸出每個(gè)元素,可以調(diào)用集合對(duì)象的forEach()方法,給它傳遞一個(gè)System.out:println方法引用。cities.forEach(System.out::println);12.3.3遍歷集合元素3.使用迭代器迭代器是一個(gè)可以遍歷集合中每個(gè)元素的對(duì)象。調(diào)用集合對(duì)象的iterator()方法可以得到Iterator對(duì)象,再調(diào)用Iterator對(duì)象的方法就可以遍歷集合中的每個(gè)元素。Iterator接口定義了如下3個(gè)方法。booleanhasNext():返回迭代器中是否還有對(duì)象。Enext():返回迭代器中下一個(gè)對(duì)象。voidremove():刪除迭代器中的當(dāng)前對(duì)象。12.3.3遍歷集合元素Iterator使用一個(gè)內(nèi)部指針,開始它指向第一個(gè)元素的前面。如果在指針的后面還有元素,hasNext()方法返回true。調(diào)用next()方法,指針將移到下一個(gè)元素,并返回該元素。remove()方法將刪除指針?biāo)傅脑?。假設(shè)myList是ArrayList的一個(gè)對(duì)象,要訪問myList中的每個(gè)元素,可以按下列方法實(shí)現(xiàn):Iteratoriterator=myList.iterator();//得到迭代器對(duì)象while(iterator.hasNext()){System.out.println(iterator.next());}使用Iterator也可以用for循環(huán)訪問集合元素。for(Iteratoriterator=myList.iterator();iterator.hasNext();){System.out.println(iterator.next());}4.雙向迭代器List還提供了listIterator()方法返回ListIterator對(duì)象。它可以從前后兩個(gè)方向遍歷線性表中元素,在迭代中修改元素以及獲得元素的當(dāng)前位置。ListIterator是Iterator的子接口,它不但繼承了Iterator接口中的方法,還定義了自己的方法。12.3.3遍歷集合元素booleanhasPrevious():是否還有前一個(gè)元素。Eprevious():返回前一個(gè)元素。voidremove():刪除當(dāng)前元素。voidset(Eo):修改當(dāng)前元素。List<String>myList=List.of("北京","上海","廣州","深圳"); ListIterator<String>iterator=myList.listIterator();while(iterator.hasNext()){iterator.next();}//從后向前訪問列表每個(gè)元素while(iterator.hasPrevious()){ System.out.print(iterator.previous()+"");}程序12.8IteratorDemo.javajava.util.Arrays類提供了一個(gè)asList()方法,它實(shí)現(xiàn)將數(shù)組轉(zhuǎn)換成List對(duì)象的功能,該方法的定義如下。publicstatic<T>List<T>asList(T…a)該方法提供了一個(gè)方便的從多個(gè)元素創(chuàng)建List對(duì)象的途徑,它的功能與Collection接口的toArray()方法相反。12.3.4數(shù)組轉(zhuǎn)換為L(zhǎng)ist對(duì)象String[]str={"one","two","three","four"};List<String>list=Arrays.asList(str);//將數(shù)組轉(zhuǎn)換為列表System.out.println(list);Java語言程序設(shè)計(jì)12.4Set接口及實(shí)現(xiàn)類Set接口是Collection的子接口,Set接口對(duì)象類似于數(shù)學(xué)上的集合概念,其中不允許有重復(fù)的元素。Set接口沒有定義新的方法,只包含從Collection接口繼承的方法。Set接口的常用實(shí)現(xiàn)類有:HashSet類、TreeSet類和LinkedHashSet類。概述HashSet類用散列方法存儲(chǔ)元素,具有最好的存取性能,但元素沒有順序。HashSet類的構(gòu)造方法有:12.4.1HashSet類HashSet類的構(gòu)造方法有:HashSet()初始容量是16,默認(rèn)裝填因子是0.75。HashSet(Collectionc)HashSet(intinitialCapacity)varwords=newHashSet<String>();words.add("one");words.add("two");words.add("three");words.add("one");for(varw:words){System.out.print(w+"");//輸出:onetwothree}使用Set對(duì)象的批量操作方法,可以實(shí)現(xiàn)標(biāo)準(zhǔn)集合代數(shù)運(yùn)算。假設(shè)s1和s2是Set對(duì)象,下面的操作可實(shí)現(xiàn)相關(guān)的集合運(yùn)算。12.4.2用Set對(duì)象實(shí)現(xiàn)集合運(yùn)算s1.addAll(s2):實(shí)現(xiàn)集合s1與s2的并運(yùn)算。s1.retainAll(s2):實(shí)現(xiàn)集合s1與s2的交運(yùn)算。s1.removeAll(s2):實(shí)現(xiàn)集合s1與s2的差運(yùn)算。s1.containAll(s2):如果s2是s1的子集,該方法返回true。設(shè)集合中存放的元素類型為Integer。為了計(jì)算兩個(gè)集合的并、交、差運(yùn)算而又不破壞原來的集合,可以通過下面代碼實(shí)現(xiàn)。vars1=Set.of(1,2,3);vars2=Set.of(2,3,4);varunion=newHashSet<Integer>(s1);union.addAll(s2);//[1,2,3,4]varintersection=newHashSet<Integer>(s1);intersection.retainAll(s2);//[2,3]vardifference=newHashSet<Integer>(s1);difference.removeAll(s2);//[1]TreeSet實(shí)現(xiàn)一種樹集合,它使用紅-黑樹為元素排序,添加到TreeSet中的元素必須是可比較的,即元素的類必須實(shí)現(xiàn)Comparable<T>接口。它的操作要比HashSet慢。
TreeSet類的默認(rèn)構(gòu)造方法創(chuàng)建一個(gè)空的樹集合,其他構(gòu)造方法如下。12.4.3TreeSet類TreeSet(Collectionc)TreeSet(Comparatorc)varts=newTreeSet<String>();//TreeSet中的元素將自動(dòng)排序vars=newString[]{"one","two","three","four"};for(vari=0;i<s.length;i++){ts.add(s[i]);}System.out.println(ts);//輸出:[four,one,three,two]
從輸出結(jié)果中可以看到,這些字符串是按照自然順序排序的。程序12.9TreeSetDemo.java創(chuàng)建TreeSet類對(duì)象時(shí)如果沒有指定比較器對(duì)象,集合中的元素按自然順序排列。所謂自然順序(naturalorder)是指集合對(duì)象實(shí)現(xiàn)了Comparable<T>接口的compareTo()方法,對(duì)象則根據(jù)該方法排序。如果試圖對(duì)沒有實(shí)現(xiàn)Comparable<T>接口的集合元素排序,將拋出ClassCastException異常。12.4.4對(duì)象順序另一種排序方法是創(chuàng)建TreeSet對(duì)象時(shí)指定一個(gè)比較器對(duì)象,這樣,元素將按比較器的規(guī)則排序。如果需要指定新的比較規(guī)則,可以定義一個(gè)類實(shí)現(xiàn)Comparator<T>接口,然后為集合提供一個(gè)新的比較器。字符串的默認(rèn)比較規(guī)則是按字母順序比較。假如按反順序比較,可以定義一個(gè)類實(shí)現(xiàn)Comparator<T>接口,然后用該類對(duì)象作為比較器。下面的程序就可以實(shí)現(xiàn)字符串的降序排序:12.4.4對(duì)象順序String[]s={"China","England","France","America","Russia",};varts=newTreeSet<String>();for(vari=0;i<s.length;i++){ts.add(s[i]);}System.out.println(ts);//按自然順序輸出程序12.10DescSortDemo.javats=newTreeSet<String>(
newComparator<String>(){
@Overridepublicintcompare(Stringa,Stringb){returnpareTo(a);}});
for(vari=0;i<s.length;i++){
ts.add(s[i]);}System.out.println(ts);創(chuàng)建一個(gè)比較器對(duì)象Java語言程序設(shè)計(jì)12.5Queue接口及實(shí)現(xiàn)類Queue接口是Collection的子接口,它是以先進(jìn)先出(First-In-First-Out,F(xiàn)IFO)的方式排列其元素,一般稱為隊(duì)列(queue)。Deque接口是Queue接口的子接口,它的對(duì)象實(shí)現(xiàn)雙端隊(duì)列,ArrayDeque和LinkedList是它的兩個(gè)實(shí)現(xiàn)類。PriorityQueue實(shí)現(xiàn)的是一種優(yōu)先隊(duì)列,優(yōu)先隊(duì)列中元素的順序是根據(jù)元素的值排列的。概述Queue接口除了提供Collection的操作外,還提供了插入、刪除和檢查操作。12.5.1Queue接口和Deque接口Deque接口實(shí)現(xiàn)雙端隊(duì)列,它支持從兩端插入和刪除元素,它同時(shí)實(shí)現(xiàn)了Stack和Queue的功能。Deque接口中定義的基本操作方法,如表12-1所示。Deque接口常用方法操作類型隊(duì)首元素操作隊(duì)尾元素操作插入元素addFirst(e)offerFirst(e)addLast(e)offerLast(e)返回元素getFirst()peekFirst()getLast()peekLast()刪除元素removeFirst()pollFirstremoveLast()pollLast()Deque的常用實(shí)現(xiàn)類包括ArrayDeque類和LinkedList類,前者是可變數(shù)組的實(shí)現(xiàn),后者是線性表的實(shí)現(xiàn)。LinkedList類比ArrayDeque類更靈活,它實(shí)現(xiàn)了線性表的所有操作,其中可以存儲(chǔ)null元素,但ArrayDeque對(duì)象不能存儲(chǔ)null。2.5.2ArrayDeque類和LinkedList類可以使用增強(qiáng)的for循環(huán)和迭代器訪問Deque的元素。使用增強(qiáng)的for循環(huán)和迭代器訪問Deque的元素。varqueue=newArrayDeque<String>();queue.add("s1");queue.add("s2");for(Stringstr:queue){System.out.print(str+"");//輸出:s1s2}使用迭代器訪問Deque元素代碼如下:for(Iterator<String>iter=queue.iterator();iter.hasNext();){System.out.print(iter.next()+"");//輸出:s1s2}可以使用LinkedList的無參數(shù)構(gòu)造方法創(chuàng)建一個(gè)空的鏈表,也可以使用LinkedList(Collectionc)構(gòu)造方法用集合c中的元素創(chuàng)建一個(gè)鏈表。LinkedList類的應(yīng)用創(chuàng)建LinkedList對(duì)象不需要指定初始容量。LinkedList類除實(shí)現(xiàn)List接口中方法外,還定義了addFirst()、getFirst()、removeFirst()、addLast()、getLast()和removeLast()等方法。注意,LinkedList同時(shí)實(shí)現(xiàn)了List接口和Queue接口。下面程序使用LinkedList類實(shí)現(xiàn)一個(gè)10秒倒計(jì)時(shí)器。程序首先將10到1存儲(chǔ)到隊(duì)列中,然后從10到1每隔1秒鐘輸出一個(gè)數(shù)。inttime=10;varqueue=newLinkedList<Integer>();for(vari=time;i>0;i--){queue.add(i);//將10到1存儲(chǔ)到隊(duì)列中}while(!queue.isEmpty()){System.out.println(queue.remove());//從隊(duì)列中刪除一個(gè)元素try{Thread.sleep(1000);//當(dāng)前線程睡眠1秒鐘}catch(InterruptedExceptione){e.printStackTrace();}}程序12.12CountDown.java集合實(shí)現(xiàn)類的構(gòu)造方法一般都接受一個(gè)Collection對(duì)象,這使得可以將Collection轉(zhuǎn)換成不同類型的集合。下面是一些實(shí)現(xiàn)類的構(gòu)造方法:12.5.3集合轉(zhuǎn)換publicArrayList(Collectionc)publicHashSet(Collectionc)publicLinkedList(Collectionc)下面代碼將一個(gè)Queue對(duì)象轉(zhuǎn)換成一個(gè)List:Queue<String>queue=newLinkedList<>();queue.add("hello");queue.add("world");List<String>myList=newArrayList(queue);以下代碼又可以將一個(gè)List對(duì)象轉(zhuǎn)換成Set對(duì)象:Set<String>set=newHashSet(myList);12.5.3集合轉(zhuǎn)換Java語言程序設(shè)計(jì)12.6案例:用集合存儲(chǔ)、遍歷員工記錄設(shè)計(jì)一個(gè)員工記錄類型Employee,該記錄只用來存儲(chǔ)數(shù)據(jù),它包括員工號(hào)(id)、姓名(name)、出生日期(birthday)和工資(salary)等4個(gè)字段。程序創(chuàng)建多個(gè)員工記錄并將其存儲(chǔ)到集合中,員工號(hào)相同的記錄視為同一個(gè)對(duì)象,不重復(fù)存儲(chǔ),遍歷集合將員工信息按員工號(hào)升序輸出到控制臺(tái)。用集合存儲(chǔ)、遍歷員工記錄(1)首先定義員工記錄類型Employee。(2)創(chuàng)建HashSet集合對(duì)象,使用泛型限定只存儲(chǔ)員工對(duì)象。(3)創(chuàng)建若干員工對(duì)象,把員工對(duì)象添加到集合。(4)遍歷集合,輸出員工信息到控制臺(tái)。(5)修改員工類使其實(shí)現(xiàn)Comparable<Employee>接口,讓對(duì)象擁有自然序。實(shí)現(xiàn)接口中的compareTo抽象方法,按員工號(hào)升序排序員工對(duì)象。(6)定義TreeSet集合,將HashSet集合中的員工對(duì)象添加到此TreeSet集合中,實(shí)現(xiàn)員工對(duì)象按員工號(hào)升序存儲(chǔ)。設(shè)計(jì)思路publicrecordEmployee(intid,Stringname,LocalDatebirthday,doublesalary)implementsComparable<Employee>{@OverridepublicintcompareTo(Employeeemployee){ if(id()>employee.id()) return1; elseif(id()<employee.id()) return-1; else return0;} }程序12.13Employee.javavaremp1=newEmployee(105,"張三",LocalDate.of(1980,12,15),6000);varemp2=newEmployee(102,"李四",LocalDate.of(2002,8,28),4000);varemp3=newEmployee(108,"王五",LocalDate.of(2001,10,10),4800);
varemp4=newEmployee(102,"李四",LocalDate.of(2002,8,28),4000);Set<Employee>employeeSet=newHashSet<Employee>();employeeSet.add(emp1);employeeSet.add(emp2);employeeSet.add(emp3);employeeSet.add(emp4);程序12.14EmployeeSet.javaSystem.out.println("未排序的員工信息:");for(Employeeemp:employeeSet){System.out.println(emp);}Set<Employee>employeeTreeSet=newTreeSet<Employee>(employeeSet);System.out.println("按id號(hào)升序排序結(jié)果:");for(Employeeemp:employeeTreeSet){System.out.println(emp);
}Java語言程序設(shè)計(jì)12.7Map接口及實(shí)現(xiàn)類Map是用來存儲(chǔ)“鍵/值”對(duì)的對(duì)象。在Map中存儲(chǔ)的關(guān)鍵字和值都必須是對(duì)象,并要求關(guān)鍵字是唯一的,而值可以重復(fù)。Map接口常用的實(shí)現(xiàn)類有HashMap類、TreeMap類、Hashtable類和LinkedHashMap類。概述Map接口是所有映射的根接口。映射實(shí)際上是“鍵/值”對(duì)的集合,比如學(xué)號(hào)和姓名的對(duì)照表。學(xué)號(hào)是鍵,姓名是值。Map接口的操作可分為兩類:基本操作和批量操作。12.7.1Map接口1.基本操作Map接口實(shí)現(xiàn)基本操作的方法包括添加“鍵/值”對(duì)、刪除“鍵/值”、返回指定鍵的值對(duì)等。publicVput(Kkey,Vvalue):向映射對(duì)象中添加一個(gè)“鍵/值”對(duì)。publicVremove(Objectkey):從映射中刪除指定鍵的“鍵/值”對(duì)。publicVget(Objectkey):返回指定鍵的值。12.7.1Map接口publicbooleancontainsKey(Objectkey):返回映射中是否包含指定的鍵。publicbooleancontainsValue(Objectvalue):返回映射中是否包含指定的值。defaultVreplace(Kkey,Vvalue):若指定的“鍵/值”對(duì)存在于映射中,用指定的“鍵/值”對(duì)替換之。defaultvoidforEach(BiConsumer<?superK,?superV):對(duì)映射中的每項(xiàng)執(zhí)行一次動(dòng)作,直到所有項(xiàng)處理完或發(fā)生異常。publicintsize():返回映射中包含的“鍵/值”對(duì)個(gè)數(shù)。publicbooleanisEmpty():返回映射是否為空。2.批量操作publicvoidputAll(Map<?extendsK,?extendsV>map):將參數(shù)map中的所有“鍵/值”對(duì)添加到映射中。publicvoidclear():刪除映射中所有“鍵/值”對(duì)。publicSet<K>keySet():返回由鍵組成的Set對(duì)象。publicCollection<V>values():返回由值組成的Collection對(duì)象。publicSet<Map.Entry<K,V>>entrySet():返回包含Map.Entry<K,V>的一個(gè)Set對(duì)象。12.7.1Map接口3.不可改變的MapMap接口提供了下面幾個(gè)方便的方法用于創(chuàng)建不可改變的Map對(duì)象。static
<K,?V>
Map<K,?V>
of?(K
k1,V
v1):返回包含單個(gè)映射的不可修改映射。static
<K,?V>
Map<K,?V>
of?(K
k1,V
v1,K
k2,V
v2):返回一個(gè)包含兩個(gè)映射的不可修改映射。其中k1、k2是鍵,v1、v2是值。另外還定義了包含多個(gè)鍵值對(duì)的of()方法,最多可有10個(gè)鍵值對(duì)。12.7.1Map接口用上述方法創(chuàng)建的Map對(duì)象是不可改變的(unmodifiable),即不能添加、刪除或更新鍵和值。Map<Integer,String>nums=Map.of(1,"one",2,"two",3,"three");for(Integerkey:nums.keySet()){ System.out.println(key+","+nums.get(key));}//nums.replace(1,"numberone");//nums.remove(2);//nums.put(4,"four");12.7.1Map接口HashMap類以散列方法存放“鍵/值”對(duì),它的不帶參數(shù)的構(gòu)造方法創(chuàng)建一個(gè)空的映射對(duì)象,下面是帶參數(shù)的構(gòu)造方法。HashMap(intcapacity):用指定初始容量和默認(rèn)裝填因子創(chuàng)建一個(gè)映射對(duì)象。HashMap(Mapm):用指定的映射對(duì)象m創(chuàng)建一個(gè)新的映射對(duì)象。2.7.2HashMap類下面程序使用HashMap存放幾個(gè)國(guó)家名稱和首都名稱對(duì)照表,國(guó)家名稱作為鍵,首都名作為值,然后對(duì)其進(jìn)行各種操作。String[]country={"中國(guó)","印度","澳大利亞",
"德國(guó)","古巴","希臘","日本"};String[]capital={"Beijing","NewDelhi","Canberra","Berlin","Havana","Athens","Tokyo"};Map<String,String>m=newTreeMap<>();
for(inti=0;i<country.length;i++){m.put(country[i],capital[i]);
}程序12.15MapDemo.javaSystem.out.println("共有"
+m.size()+"個(gè)國(guó)家:");System.out.println(m);System.out.println(m.get("中國(guó)"));//Beijingm.remove("日本");Set<String>coun=m.keySet();
for(Objectc:coun)System.out.print(c+"");TreeMap類實(shí)現(xiàn)了SortedMap接口,它保證Map中的“鍵/值”對(duì)按關(guān)鍵字升序排序。TreeMap類的構(gòu)造方法如下。TreeMap():創(chuàng)建根據(jù)鍵的自然順序排序的空的映射。TreeMap(Comparatorc):根據(jù)給定的比較器創(chuàng)建一個(gè)空的映射。
TreeMap(Mapm):用指定的映射m創(chuàng)建新的映射,根據(jù)鍵的自然順序排序。12.7.3TreeMap類對(duì)程序12.15的例子,如果希望按國(guó)家名的順序輸出Map對(duì)象,僅將HashMap改為TreeMap即可。輸出結(jié)果將為:{Australia=堪培拉,China=北京,Cuba=哈瓦那,Germany=柏林,Greece=雅典,India=新德里,Japan=東京}在Map對(duì)象鍵上迭代可以使用增強(qiáng)的for循環(huán),也可以使用迭代器,如下所示。for(Stringkey:map.keySet()){System.out.println(key);}12.7.4在Map鍵和值上迭代如果使用迭代器,可通過下面方式實(shí)現(xiàn):for(Iterator<String>it=map.keySet().iterator();it.hasNext();){if(it.next().equals("Japan")){it.remove();}}在值上迭代與在鍵上迭代類似??梢允褂胿alues()方法得到值的Collection對(duì)象,然后在其上迭代。for(Integervalue:map.values()){System.out.println(value);}12.7.4在Map鍵和值上迭代要迭代映射中的“鍵/值”對(duì),可使用entrySet()方法返回Set<Map.Entry<K,V>>集合。Map.Entry是Map接口定義的一個(gè)內(nèi)部接口,其中定義了3個(gè)抽象方法,分別為getKey()、getValue()和setValue()方法。Hashtable類是Java早期版本提供的一個(gè)存放“鍵/值”對(duì)的實(shí)現(xiàn)類,它實(shí)現(xiàn)了一種散列表,現(xiàn)在也屬于集合框架。Hashtable類的方法都是同步的,因此它是線程安全的。12.7.5Hashtable類和Enumeration接口任何非null對(duì)象都可以作為散列表的關(guān)鍵字和值。但是要求作為關(guān)鍵字的對(duì)象必須實(shí)現(xiàn)hashCode()方法和equals()方法,以使對(duì)象的比較成為可能。下面代碼創(chuàng)建一個(gè)包含數(shù)字的Hashtable對(duì)象,使用數(shù)字作為鍵,數(shù)字的英文名稱作為值。varnumbers=newHashtable<Integer,String>();numbers.put(Integer.valueOf(1),"one");numbers.put(Integer.valueOf(2),"two");numbers.put(Integer.valueOf(3),"three");要檢索其中的數(shù)字描述,可以使用下面代碼:Strings=numbers.get(2);//返回鍵為2的值if(s!=null){System.out.println("2="+s);//輸出:2=two}Java語言程序設(shè)計(jì)12.8Collections類java.util.Collections類是工具類,它提供了若干static方法實(shí)現(xiàn)集合對(duì)象的操作。這些操作大多對(duì)List操作,主要包括下面幾個(gè)方面:排序、重排、查找、求極值以及常規(guī)操作等。Collections類對(duì)List對(duì)象的元素排序使用sort()方法,它有下面兩種格式:static<T>voidsort(List<T>list)static<T>voidsort(List<T>list,Comparator<?superT>c)1.元素排序該方法實(shí)現(xiàn)對(duì)List的元素按升序或指定的比較器順序排序。該方法使用優(yōu)化的歸并排序算法,因此排序是快速的和穩(wěn)定的。在排序時(shí)如果沒有提供Comparator<T>對(duì)象,則要求List中的對(duì)象必須實(shí)現(xiàn)Comparable<T>接口。下面代碼對(duì)字符串List倒序排序。Listnames=Arrays.asList("peter","anna","mike","tom");Collections.sort(names,newComparator<String>(){
@Overridepublicintcompare(Stringa,Stringb){ returnpareTo(a); }});for(Stringname:names){System.out.print(name+"");//輸出:tompetermikeanna}使用binarySearch()方法可以在已排序的List中查找指定的元素,方法格式如下:static<T>intbinarySearch(List<T>list,Tkey)st
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。
最新文檔
- 2025至2030年中國(guó)膜專用清洗劑數(shù)據(jù)監(jiān)測(cè)研究報(bào)告
- 政府電梯維保投標(biāo)施工方案
- 2025年中國(guó)小型手動(dòng)夾緊震蕩器市場(chǎng)調(diào)查研究報(bào)告
- Unit 1 Making friends Lesson 2(教學(xué)設(shè)計(jì))-2024-2025學(xué)年人教PEP版(2024)英語三年級(jí)上冊(cè)
- 2025年中國(guó)圓網(wǎng)印花烘干機(jī)市場(chǎng)調(diào)查研究報(bào)告
- 2025年中國(guó)疊層片式電感器市場(chǎng)調(diào)查研究報(bào)告
- 2025年中國(guó)仿真盆栽市場(chǎng)調(diào)查研究報(bào)告
- 第21課《莊子二則:北冥有魚》教學(xué)設(shè)計(jì) 2023-2024學(xué)年統(tǒng)編版語文八年級(jí)下冊(cè)
- 2024-2025學(xué)年高中化學(xué)第二章化學(xué)反應(yīng)速率和化學(xué)平衡第二節(jié)影響化學(xué)反應(yīng)速率的因素跟蹤訓(xùn)練含解析新人教版選修4
- 2024-2025學(xué)年高中物理1.4實(shí)驗(yàn):用打點(diǎn)計(jì)時(shí)器測(cè)速度教案新人教版必修1
- 豇豆生產(chǎn)技術(shù)規(guī)程
- MES運(yùn)行管理辦法
- 中藥炮制學(xué)教材
- 現(xiàn)場(chǎng)快速反應(yīng)跟蹤管理看板
- 框架核心筒結(jié)構(gòu)辦公樓施工測(cè)量方案(12頁)
- 常見腫瘤AJCC分期手冊(cè)第八版(中文版)
- 整體機(jī)房維護(hù)方案及報(bào)價(jià)通用
- 電氣第一種第二種工作票講解pptx課件
- 北大金融學(xué)課程表
- 英國(guó)簽證戶口本翻譯模板(共4頁)
- 現(xiàn)金調(diào)撥業(yè)務(wù)
評(píng)論
0/150
提交評(píng)論