常見(jiàn)問(wèn)題匯編">Spring MVC DispatcherServlet改造為 CSE RestServlet 常見(jiàn)問(wèn)題匯編
681
2025-04-01
棧Stack
堆棧是“后進(jìn)先出”(LIFO)集合。它有時(shí)被稱為疊加棧(pushdown stack),因?yàn)樽詈蟆皦喝搿保╬ush)棧的元素,第一個(gè)被“彈出”(pop)棧。經(jīng)常用來(lái)類比棧的事物是帶有彈簧支架的自助餐廳托盤。最后裝入的托盤總是最先拿出來(lái)使用的。
Java 1.0 中附帶了一個(gè) Stack 類,結(jié)果設(shè)計(jì)得很糟糕(為了向后兼容,永遠(yuǎn)堅(jiān)持 Java 中的舊設(shè)計(jì)錯(cuò)誤)。
Java 6 添加了 ArrayDeque ,其中包含直接實(shí)現(xiàn)堆棧功能的方法.
即使它是作為一個(gè)堆棧在使用,我們?nèi)匀槐仨殞⑵渎暶鳛?Deque 。有時(shí)一個(gè)名為 Stack 的類更能把事情講清楚:
基本上,這個(gè)類是在聲明“我們?cè)诙x一個(gè)可以持有 T 類型對(duì)象的 Stack 。” Stack 是使用 ArrayDeque 實(shí)現(xiàn)的,而 ArrayDeque 也被告知它將持有 T 類型對(duì)象。
push() 接受類型為 T 的對(duì)象
peek() 和 pop() 返回類型為 T 的對(duì)象
peek() 方法將返回棧頂元素,但并不將其從棧頂刪除
pop() 刪除并返回頂部元素
如果只需要棧的行為,使用繼承是不合適的,因?yàn)檫@將產(chǎn)生一個(gè)具有 ArrayDeque 的其它所有方法的類(Java 1.0 設(shè)計(jì)者在創(chuàng)建 java.util.Stack 時(shí),就犯了這個(gè)錯(cuò)誤)。
使用組合,可以選擇要公開(kāi)的方法以及如何命名它們。
盡管已經(jīng)有了 java.util.Stack ,但是 ArrayDeque 可以產(chǎn)生更好的 Stack ,因此更可取。
可以使用顯式導(dǎo)入來(lái)控制對(duì)“首選” Stack 實(shí)現(xiàn)的選擇:
import com.javaedge.Stack;
現(xiàn)在,任何對(duì) Stack 的引用都將選擇 onjava 版本,而在選擇 java.util.Stack 時(shí),必須使用全限定名稱.
Set
Set 不保存重復(fù)的元素.Set 最常見(jiàn)的用途是測(cè)試歸屬性,可以很輕松地詢問(wèn)某個(gè)對(duì)象是否在一個(gè) Set 中。因此,查找通常是 Set 最重要的操作,因此通常會(huì)選擇 HashSet 實(shí)現(xiàn),該實(shí)現(xiàn)針對(duì)快速查找進(jìn)行了優(yōu)化。
Set 與 Collection 擁有相同接口,因此無(wú)任何額外功能,不像前面兩種不同類型的 List 。實(shí)際上, Set 就是一個(gè) Collection ?,只是行為不同。
這是繼承和多態(tài)思想的典型應(yīng)用:表現(xiàn)不同的行為.
Set 根據(jù)對(duì)象的“值”確定歸屬性.
早期 Java 版本中的 HashSet 產(chǎn)生的輸出沒(méi)有可辨別的順序。這是因?yàn)槌鲇趯?duì)速度的追求, HashSet 使用了散列。由 HashSet 維護(hù)的順序與 TreeSet 或 LinkedHashSet 不同,因?yàn)樗鼈兊膶?shí)現(xiàn)具有不同的元素存儲(chǔ)方式。
TreeSet 將元素存儲(chǔ)在紅-黑樹(shù)數(shù)據(jù)結(jié)構(gòu)中,而 HashSet 使用散列函數(shù)。
LinkedHashSet也使用了散列,使用了鏈表來(lái)維護(hù)元素的插入順序。看起來(lái)散列算法好像已經(jīng)改變了,現(xiàn)在 Integer 按順序排序。
要對(duì)結(jié)果進(jìn)行排序,一種方法是使用 TreeSet 而不是 HashSet :
最常見(jiàn)的操作之一是使用 contains() 測(cè)試成員歸屬性,但也有一些其它操作
能夠產(chǎn)生每個(gè)元素都唯一的列表是相當(dāng)有用的功能。
排序是按字典順序(lexicographically)完成的,因此大寫和小寫字母位于不同的組中。如果想按字母順序(alphabetically)對(duì)其進(jìn)行排序,可以向 ?TreeSet 構(gòu)造器傳入 String.CASE_INSENSITIVE_ORDER 比較器.
Map
將對(duì)象映射到其他對(duì)象。
Map 與數(shù)組和其他的 Collection 一樣,可以輕松地?cái)U(kuò)展到多個(gè)維度,只需要?jiǎng)?chuàng)建一個(gè)值為 Map 的 Map(這些 Map 的值可以是其他集合,甚至是其他 Map)。因此,能夠很容易地將集合組合起來(lái)以快速生成強(qiáng)大的數(shù)據(jù)結(jié)構(gòu)。
例如,假設(shè)你正在追蹤有多個(gè)寵物的人,只需要一個(gè) Map\ Map 可返回由其鍵組成的 Set ,由其值組成的 Collection ,或者其鍵值對(duì)的 Set 。 keySet() 方法生成所有鍵組成的 Set ,它在 for-in 語(yǔ)句中被用來(lái)遍歷該 Map 。 隊(duì)列Queu 先進(jìn)先出集合。 即從集合的一端放入事物,再?gòu)牧硪欢巳カ@取它們,事物放入集合的順序和被取出的順序是相同的。 隊(duì)列通常被當(dāng)做一種可靠的將對(duì)象從程序的某個(gè)區(qū)域傳輸?shù)搅硪粋€(gè)區(qū)域的途徑。隊(duì)列在并發(fā)編程中尤為重要,因?yàn)樗鼈兛梢园踩貙?duì)象從一個(gè)任務(wù)傳輸?shù)搅硪粋€(gè)任務(wù)。 LinkedList 實(shí)現(xiàn) Queue 接口,并提供了一些方法以支持隊(duì)列,因此 LinkedList 可用作 Queue 的一種實(shí)現(xiàn)。 通過(guò)將 LinkedList 向上轉(zhuǎn)換為 Queue . offer() 是與 Queue 相關(guān)的方法之一,它在允許的情況下,在隊(duì)列的尾部插入一個(gè)元素,或者返回 false 。 peek() 和 element() 都返回隊(duì)頭元素而不刪除它,但是如果隊(duì)列為空,則 element() 拋出 NoSuchElementException peek() 返回 null poll() 和 remove()都刪除并返回隊(duì)頭元素,但如果隊(duì)列為空 poll() 返回 null remove() 拋出 NoSuchElementException Queue 接口窄化了對(duì) LinkedList 方法的訪問(wèn)權(quán)限,因此只有適當(dāng)?shù)姆椒ú拍苁褂茫虼四軌蛟L問(wèn)到的 LinkedList 的方法會(huì)變少(實(shí)際上可以將 Queue 強(qiáng)制轉(zhuǎn)換回 LinkedList ,但不鼓勵(lì)這樣做)。 與 Queue 相關(guān)的方法提供了完整而獨(dú)立的功能。 也就是說(shuō),對(duì)于 Queue 所繼承的 Collection ,在不需要使用它的任何方法的情況下,就可以擁有一個(gè)可用的 Queue 。 優(yōu)先級(jí)隊(duì)列PriorityQueue 先進(jìn)先出描述了最典型的隊(duì)列規(guī)則(queuing discipline)。隊(duì)列規(guī)則是指在給定隊(duì)列中的一組元素的情況下,確定下一個(gè)彈出隊(duì)列的元素的規(guī)則。 先進(jìn)先出聲明的是下一個(gè)彈出的元素應(yīng)該是等待時(shí)間最長(zhǎng)的元素。 優(yōu)先級(jí)隊(duì)列聲明下一個(gè)彈出的元素是最需要的元素(具有最高的優(yōu)先級(jí))。例如,在機(jī)場(chǎng),當(dāng)飛機(jī)臨近起飛時(shí),這架飛機(jī)的乘客可以在辦理登機(jī)手續(xù)時(shí)排到隊(duì)頭。如果構(gòu)建了一個(gè)消息傳遞系統(tǒng),某些消息比其他消息更重要,應(yīng)該盡快處理,而不管它們何時(shí)到達(dá)。在Java5 中添加了 PriorityQueue ,以便自動(dòng)實(shí)現(xiàn)這種行為。 當(dāng)在 PriorityQueue 上調(diào)用 offer() 方法來(lái)插入一個(gè)對(duì)象時(shí),該對(duì)象會(huì)在隊(duì)列中被排序。[^5]默認(rèn)的排序使用隊(duì)列中對(duì)象的自然順序(natural order),但是可以通過(guò)提供自己的 Comparator 來(lái)修改這個(gè)順序。 PriorityQueue 確保在調(diào)用 peek() , poll() 或 remove() 方法時(shí),獲得的元素將是隊(duì)列中優(yōu)先級(jí)最高的元素。 PriorityQueue 允許重復(fù),最小的值具有最高的優(yōu)先級(jí)(如果是 String ,空格也可以算作值,并且比字母的優(yōu)先級(jí)高)。 Integer , String 和 Character 可以與 PriorityQueue 一起使用,因?yàn)檫@些類已經(jīng)內(nèi)置了自然排序。如果想在 PriorityQueue 中使用自己的類,則必須包含額外的方法以產(chǎn)生自然排序,或者必須提供自己的 Comparator 。 集合 VS 迭代器 Collection 是所有序列集合的根接口。它可能會(huì)被認(rèn)為是一種“附屬接口”,即因?yàn)橐硎酒渌舾蓚€(gè)接口的共性而出現(xiàn)的接口。 java.util.AbstractCollection 類提供了 Collection 的默認(rèn)實(shí)現(xiàn),使得你可以創(chuàng)建 AbstractCollection 的子類型,而其中沒(méi)有不必要的代碼重復(fù)。 使用接口描述的一個(gè)理由是它可以使我們創(chuàng)建更通用的代碼。通過(guò)針對(duì)接口而非具體實(shí)現(xiàn)來(lái)編寫代碼,我們的代碼可以應(yīng)用于更多類型的對(duì)象。因此,如果所編寫的方法接受一個(gè) Collection ,那么該方法可以應(yīng)用于任何實(shí)現(xiàn)了 Collection 的類——這也就使得一個(gè)新類可以選擇去實(shí)現(xiàn) Collection 接口,以便該方法可以使用它。 標(biāo)準(zhǔn) C++ 類庫(kù)中的的集合并沒(méi)有共同的基類——集合之間的所有共性都是通過(guò)迭代器實(shí)現(xiàn)的。在 Java 中,遵循 C++ 的方式看起來(lái)似乎很明智,即用迭代器而不是 Collection 來(lái)表示集合之間的共性。 但是, Java中這兩種方法綁定在了一起,因此實(shí)現(xiàn) Collection 就意味著需要提供 iterator() 方法. 都可以使用 Map 或 Collection 的子類型來(lái)工作。 而且Collection 接口和 Iterator 都將方法與底層集合的特定實(shí)現(xiàn)解耦。 事實(shí)上, Collection 要更方便一點(diǎn),因?yàn)樗?Iterable 類型,因此在 display(Collection) 的實(shí)現(xiàn)中可以使用 for-in 構(gòu)造,這使得代碼更加清晰。 當(dāng)需要實(shí)現(xiàn)一個(gè)不是 Collection 的外部類時(shí),由于讓它去實(shí)現(xiàn) Collection 接口可能非常困難或麻煩,因此使用 Iterator 就會(huì)變得非常吸引人。 例如,如果我們通過(guò)繼承一個(gè)持有 Pet 對(duì)象的類來(lái)創(chuàng)建一個(gè) Collection 的實(shí)現(xiàn),那么我們必須實(shí)現(xiàn) Collection 所有的方法,即使我們不在 display() 方法中使用它們,也必須這樣做。雖然這可以通過(guò)繼承 AbstractCollection 而很容易地實(shí)現(xiàn),但是無(wú)論如何還是要被強(qiáng)制去實(shí)現(xiàn) iterator() 和 size() 方法,這些方法 AbstractCollection 沒(méi)有實(shí)現(xiàn),但是 AbstractCollection 中的其它方法會(huì)用到: 你可能會(huì)認(rèn)為,因?yàn)?iterator() 返回 Iterator\ ,匿名內(nèi)部類定義可以使用菱形語(yǔ)法,Java可以推斷出類型。但這不起作用,類型推斷仍然非常有限。 如果實(shí)現(xiàn) Collection ,就必須實(shí)現(xiàn) iterator() ,并且只拿實(shí)現(xiàn) iterator() 與繼承 AbstractCollection 相比,花費(fèi)的代價(jià)只略微減少。 但是,如果類已經(jīng)繼承其他類,就不能再繼承 AbstractCollection 。這種情況,要實(shí)現(xiàn) Collection ,就必須實(shí)現(xiàn)該接口中的所有方法。此時(shí),繼承并提供創(chuàng)建迭代器的能力要容易得多. 生成 Iterator 是將序列與消費(fèi)該序列的方法連接在一起耦合度最小的方式,并且與實(shí)現(xiàn)Collection 相比,它在序列類上所施加的約束也少。 for-in和迭代器 for-in 語(yǔ)法主要用于數(shù)組,但它也適用于任何 Collection 對(duì)象。 原因是 Java 5 引入了一個(gè)名為 Iterable 的接口,該接口包含一個(gè)能夠生成 Iterator 的 iterator() 方法。for-in 使用此 Iterable 接口來(lái)遍歷序列。因此,如果創(chuàng)建了任何實(shí)現(xiàn)了 Iterable 的類,都可以將它用于 for-in 語(yǔ)句中: iterator() 返回的是實(shí)現(xiàn)了 Iterator\ 的匿名內(nèi)部類的實(shí)例,該匿名內(nèi)部類可以遍歷數(shù)組中的每個(gè)單詞。在主方法中,可以看到 IterableClass 確實(shí)可以用于 for-in 語(yǔ)句。 在 Java 5 中,許多類都是 Iterable ,主要包括所有的 Collection 類(但不包括各種 Maps )。 for-in 語(yǔ)句適用于數(shù)組或其它任何 Iterable ,但這并不意味著數(shù)組肯定也是個(gè) Iterable ,也不會(huì)發(fā)生任何自動(dòng)裝箱.嘗試將數(shù)組作為一個(gè) Iterable 參數(shù)傳遞會(huì)導(dǎo)致失敗。這說(shuō)明不存在任何從數(shù)組到 Iterable 的自動(dòng)轉(zhuǎn)換; 必須手工執(zhí)行這種轉(zhuǎn)換。 適配器方法慣用法 如果現(xiàn)在有一個(gè) Iterable 類,你想要添加一種或多種在 for-in 語(yǔ)句中使用這個(gè)類的方法,應(yīng)該怎么做呢?例如,你希望可以選擇正向還是反向遍歷一個(gè)單詞列表。如果直接繼承這個(gè)類,并覆蓋 iterator() 方法,則只能替換現(xiàn)有的方法,而不能實(shí)現(xiàn)遍歷順序的選擇。 一種解決方案是所謂適配器方法(Adapter Method)的慣用法。“適配器”部分來(lái)自于設(shè)計(jì)模式,因?yàn)楸仨氁峁┨囟ǖ慕涌趤?lái)滿足 for-in 語(yǔ)句。如果已經(jīng)有一個(gè)接口并且需要另一個(gè)接口時(shí),則編寫適配器就可以解決這個(gè)問(wèn)題。 在這里,若希望在默認(rèn)的正向迭代器的基礎(chǔ)上,添加產(chǎn)生反向迭代器的能力,因此不能使用覆蓋,相反,而是添加了一個(gè)能夠生成 Iterable 對(duì)象的方法,該對(duì)象可以用于 for-in 語(yǔ)句。 小結(jié) Java 提供了許多保存對(duì)象的方法: 數(shù)組將數(shù)字索引與對(duì)象相關(guān)聯(lián)。它保存類型明確的對(duì)象,因此在查找對(duì)象時(shí)不必對(duì)結(jié)果做類型轉(zhuǎn)換。它可以是多維的,可以保存基本類型的數(shù)據(jù)。雖然可以在運(yùn)行時(shí)創(chuàng)建數(shù)組,但是一旦創(chuàng)建數(shù)組,就無(wú)法更改數(shù)組的大小 Collection 保存單一元素,而 Map 包含相關(guān)聯(lián)的鍵值對(duì)。使用 Java 泛型,可以指定集合中保存的對(duì)象的類型,因此不能將錯(cuò)誤類型的對(duì)象放入集合中,并且在從集合中獲取元素時(shí),不必進(jìn)行類型轉(zhuǎn)換。各種 Collection 和各種 Map 都可以在你向其中添加更多的元素時(shí),自動(dòng)調(diào)整其尺寸大小。集合不能保存基本類型,但自動(dòng)裝箱機(jī)制會(huì)負(fù)責(zé)執(zhí)行基本類型和集合中保存的包裝類型之間的雙向轉(zhuǎn)換 像數(shù)組一樣, List 也將數(shù)字索引與對(duì)象相關(guān)聯(lián),因此,數(shù)組和 List 都是有序集合 如果要執(zhí)行大量的隨機(jī)訪問(wèn),則使用 ArrayList ,如果要經(jīng)常從表中間插入或刪除元素,則應(yīng)該使用 LinkedList 隊(duì)列和堆棧的行為是通過(guò) LinkedList 提供的 Map 是一種將對(duì)象(而非數(shù)字)與對(duì)象相關(guān)聯(lián)的設(shè)計(jì)。 HashMap 專為快速訪問(wèn)而設(shè)計(jì),而 TreeMap 保持鍵始終處于排序狀態(tài),所以沒(méi)有 HashMap 快。LinkedHashMap 按插入順序保存其元素,但使用散列提供快速訪問(wèn)的能力 Set 不接受重復(fù)元素。 HashSet 提供最快的查詢速度,而 TreeSet 保持元素處于排序狀態(tài)。 LinkedHashSet 按插入順序保存其元素,但使用散列提供快速訪問(wèn)的能力 不要在新代碼中使用遺留類 Vector ,Hashtable 和 Stack 簡(jiǎn)單集合分類 實(shí)際上只有四個(gè)基本的集合組件: Map , List , Set 和 Queue ,它們各有兩到三個(gè)實(shí)現(xiàn)版本(Queue 的 java.util.concurrent 實(shí)現(xiàn)未包含在此圖中)。最常使用的集合用黑色粗線線框表示。 虛線框表示接口,實(shí)線框表示普通的(具體的)類。帶有空心箭頭的虛線表示特定的類實(shí)現(xiàn)了一個(gè)接口。實(shí)心箭頭表示某個(gè)類可以生成箭頭指向的類的對(duì)象。例如,任何 Collection 都可以生成 Iterator , List 可以生成 ListIterator (也能生成普通的 Iterator ,因?yàn)?List 繼承自 Collection )。 除 TreeSet 之外的所有 Set 都具有與 Collection 完全相同的接口。List 和 Collection 存在著明顯的不同,盡管 List 所要求的方法都在 Collection 中。另一方面,在 Queue 接口中的方法是獨(dú)立的,在創(chuàng)建具有 Queue 功能的實(shí)現(xiàn)時(shí),不需要使用 Collection 方法。最后, Map 和 Collection 之間唯一的交集是 Map 可以使用 entrySet() 和 values() 方法來(lái)產(chǎn)生 Collection 。 請(qǐng)注意,標(biāo)記接口 java.util.RandomAccess 附加到了 ArrayList 上,但不附加到 LinkedList 上。這為根據(jù)特定 List 動(dòng)態(tài)改變其行為的算法提供了信息。 從面向?qū)ο蟮睦^承層次結(jié)構(gòu)來(lái)看,這種組織結(jié)構(gòu)確實(shí)有些奇怪。但是,當(dāng)了解了 java.util 中更多的有關(guān)集合的內(nèi)容后,就會(huì)發(fā)現(xiàn)出了繼承結(jié)構(gòu)有點(diǎn)奇怪外,還有更多的問(wèn)題。集合類庫(kù)一直以來(lái)都是設(shè)計(jì)難題——解決這些問(wèn)題涉及到要去滿足經(jīng)常彼此之間互為牽制的各方面需求。所以要做好準(zhǔn)備,在各處做出妥協(xié)。 盡管存在這些問(wèn)題,但 Java 集合仍是在日常工作中使用的基本工具,它可以使程序更簡(jiǎn)潔、更強(qiáng)大、更有效。 Java 容器
版權(quán)聲明:本文內(nèi)容由網(wǎng)絡(luò)用戶投稿,版權(quán)歸原作者所有,本站不擁有其著作權(quán),亦不承擔(dān)相應(yīng)法律責(zé)任。如果您發(fā)現(xiàn)本站中有涉嫌抄襲或描述失實(shí)的內(nèi)容,請(qǐng)聯(lián)系我們jiasou666@gmail.com 處理,核實(shí)后本網(wǎng)站將在24小時(shí)內(nèi)刪除侵權(quán)內(nèi)容。
版權(quán)聲明:本文內(nèi)容由網(wǎng)絡(luò)用戶投稿,版權(quán)歸原作者所有,本站不擁有其著作權(quán),亦不承擔(dān)相應(yīng)法律責(zé)任。如果您發(fā)現(xiàn)本站中有涉嫌抄襲或描述失實(shí)的內(nèi)容,請(qǐng)聯(lián)系我們jiasou666@gmail.com 處理,核實(shí)后本網(wǎng)站將在24小時(shí)內(nèi)刪除侵權(quán)內(nèi)容。