Map接口
Map是將鍵映射到值的對象,map不能包含重復(fù)的鍵:每個鍵最多可以映射一個值,它模擬數(shù)學(xué)函數(shù)抽象。Map接口包括基本操作的方法(如put、get、remove、containsKey、containsValue、size和empty),批量操作(如putAll和clear)和集合視圖(如keySet、entrySet和values)。
Java平臺包含三個通用Map實現(xiàn):HashMap、TreeMap和LinkedHashMap,它們的行為和性能完全類似于HashSet、TreeSet和LinkedHashSet,如Set接口部分所述。
本頁的其余部分詳細討論了Map接口,但首先,這里有一些使用JDK 8聚合操作收集到Map的示例,對現(xiàn)實世界對象進行建模是面向?qū)ο缶幊讨械某R娙蝿?wù),因此可以合理地認為某些程序可能會按部門對員工進行分組:
// Group employees by department Map> byDept = employees.stream() .collect(Collectors.groupingBy(Employee::getDepartment));
或者按部門計算所有工資的總和:
// Compute sum of salaries by department MaptotalByDept = employees.stream() .collect(Collectors.groupingBy(Employee::getDepartment, Collectors.summingInt(Employee::getSalary)));
或者通過成績及格或成績不及格分組學(xué)生:
// Partition students into passing and failing Map> passingFailing = students.stream() .collect(Collectors.partitioningBy(s -> s.getGrade()>= PASS_THRESHOLD));
你還可以按城市分組:
// Classify Person objects by city Map> peopleByCity = personStream.collect(Collectors.groupingBy(Person::getCity));
或者甚至級聯(lián)兩個收集器按州和城市對人進行分類:
// Cascade Collectors Map>> peopleByStateAndCity = personStream.collect(Collectors.groupingBy(Person::getState, Collectors.groupingBy(Person::getCity)))
同樣,這些只是如何使用新JDK 8 API的幾個示例,有關(guān)lambda表達式和聚合操作的深入介紹,請參閱標(biāo)題為聚合操作的課程。
Map接口基本操作Map(put、get、containsKey、containsValue、size和isEmpty)的基本操作與Hashtable中的對應(yīng)操作完全相同,以下程序生成其參數(shù)列表中找到的單詞的頻率表,頻率表將每個單詞映射到它在參數(shù)列表中出現(xiàn)的次數(shù)。
import java.util.*; public class Freq { public static void main(String[] args) { Mapm = new HashMap (); // Initialize frequency table from command line for (String a : args) { Integer freq = m.get(a); m.put(a, (freq == null) ? 1 : freq + 1); } System.out.println(m.size() + " distinct words:"); System.out.println(m); } }
關(guān)于這個程序唯一棘手的問題是put語句的第二個參數(shù),該參數(shù)是一個條件表達式,如果單詞之前從未出現(xiàn)過,則其頻率設(shè)置為1,如果單詞已經(jīng)出現(xiàn),則其頻率設(shè)置為當(dāng)前值加1,嘗試使用以下命令運行此程序:
java Freq if it is to be it is up to me to delegate
該程序產(chǎn)生以下輸出。
8 distinct words: {to=3, delegate=1, be=1, it=2, up=1, if=1, me=1, is=2}
假設(shè)你希望按字母順序查看頻率表,你所要做的就是將Map的實現(xiàn)類型從HashMap更改為TreeMap,進行這種更改會導(dǎo)致程序從同一命令行生成以下輸出。
8 distinct words: {be=1, delegate=1, if=1, is=2, it=2, me=1, to=3, up=1}
類似地,你可以通過將map的實現(xiàn)類型更改為LinkedHashMap,使程序按照單詞首次出現(xiàn)在命令行上的順序打印頻率表,這樣做會產(chǎn)生以下輸出。
8 distinct words: {if=1, it=2, is=2, to=3, be=1, up=1, me=1, delegate=1}
這種靈活性提供了基于接口的框架功能的有力說明。
與Set和List接口一樣,Map強化了對equals和hashCode方法的要求,因此可以比較兩個Map對象的邏輯相等性,而不考慮它們的實現(xiàn)類型,如果兩個Map實例表示相同的鍵值映射,則它們是相等的。
按照慣例,所有通用Map實現(xiàn)都提供構(gòu)造函數(shù),這些構(gòu)造函數(shù)接受Map對象并初始化新Map以包含指定Map中的所有鍵值映射。這個標(biāo)準(zhǔn)的Map轉(zhuǎn)換構(gòu)造函數(shù)完全類似于標(biāo)準(zhǔn)的Collection構(gòu)造函數(shù):它允許調(diào)用者創(chuàng)建一個所需實現(xiàn)類型的Map,該Map最初包含另一個Map中的所有映射,而不管其他Map的實現(xiàn)類型如何。例如,假設(shè)你有一個名為m的Map,以下單行創(chuàng)建一個新的HashMap,最初包含與m相同的所有鍵值映射。
MapMap接口批量操作copy = new HashMap (m);
clear的操作完全符合你的想法:它從Map中刪除所有映射。putAll操作是Collection接口的addAll操作的Map模擬,除了明顯使用將一個Map轉(zhuǎn)儲到另一個Map之外,它還有第二個更微妙的用途,假設(shè)Map用于表示屬性—值對的集合,putAll操作與Map轉(zhuǎn)換構(gòu)造函數(shù)結(jié)合使用,提供了一種使用默認值實現(xiàn)屬性映射創(chuàng)建的簡潔方法。以下是演示此技術(shù)的靜態(tài)工廠方法。
static集合視圖Map newAttributeMap(Map defaults, Map overrides) { Map result = new HashMap (defaults); result.putAll(overrides); return result; }
Collection視圖方法允許以這三種方式將Map視為Collection:
keySet — Map中包含鍵的Set。
values — Map中包含值的Collection,此Collection不是Set,因為多個鍵可以映射到相同的值。
entrySet — Map中包含的鍵值對的Set,Map接口提供了一個名為Map.Entry的小型嵌套接口,該接口是此Set中元素的類型。
Collection視圖提供迭代Map的唯一方法,此示例說明了使用for-each構(gòu)造迭代Map中的鍵的標(biāo)準(zhǔn)語法:
for (KeyType key : m.keySet()) System.out.println(key);
使用迭代器:
// Filter a map based on some // property of its keys. for (Iteratorit = m.keySet().iterator(); it.hasNext(); ) if (it.next().isBogus()) it.remove();
迭代值的語法是類似的,以下是迭代鍵值對的語法。
for (Map.Entrye : m.entrySet()) System.out.println(e.getKey() + ": " + e.getValue());
起初,許多人擔(dān)心這些語法可能會很慢,因為每次調(diào)用Collection視圖操作時Map都必須創(chuàng)建一個新的Collection實例,放松:每次要求給定的Collection視圖時,Map都沒有理由不能總是返回相同的對象,這正是java.util中所有Map實現(xiàn)的功能。
對于所有這三個Collection視圖,調(diào)用Iterator的remove操作將從支持Map中刪除相關(guān)條目,假設(shè)支持Map一開始就支持元素刪除,這由前面的過濾語法說明。
使用entrySet視圖,還可以通過在迭代期間調(diào)用Map.Entry的setValue方法來更改與鍵關(guān)聯(lián)的值(同樣,假設(shè)Map一開始就支持值修改)。請注意,這些是在迭代期間修改Map的唯一安全方法,如果在迭代進行過程中以任何其他方式修改基礎(chǔ)Map,則行為是未指定的。
Collection視圖支持以多種形式刪除元素 — remove、removeAll、retainAll和clear操作,以及Iterator.remove操作(同樣,這假設(shè)支持Map支持元素刪除)。
Collection視圖在任何情況下都不支持元素添加,對于keySet和values視圖沒有任何意義,并且對于entrySet視圖沒有必要,因為支持Map的put和putAll方法提供相同的功能。
Collection視圖的花哨用途:Map代數(shù)應(yīng)用于Collection視圖時,批量操作(containsAll、removeAll和retainAll)是令人驚訝的強大工具。對于初學(xué)者,假設(shè)你想知道一個Map是否是另一個Map的子圖 — 也就是說,第一個Map是否包含第二個Map中的所有鍵值映射,以下語法可以解決這個問題。
if (m1.entrySet().containsAll(m2.entrySet())) { ... }
沿著類似的路線,假設(shè)你想知道兩個Map對象是否包含所有相同鍵的映射。
if (m1.keySet().equals(m2.keySet())) { ... }
假設(shè)你有一個表示屬性—值對集合的Map,以及兩個表示所需屬性和允許屬性的Set(允許的屬性包括必需的屬性),以下代碼段確定屬性映射是否符合這些約束,如果不符合則打印詳細的錯誤消息。
staticboolean validate(Map attrMap, Set requiredAttrs, Set permittedAttrs) { boolean valid = true; Set attrs = attrMap.keySet(); if (! attrs.containsAll(requiredAttrs)) { Set missing = new HashSet (requiredAttrs); missing.removeAll(attrs); System.out.println("Missing attributes: " + missing); valid = false; } if (! permittedAttrs.containsAll(attrs)) { Set illegal = new HashSet (attrs); illegal.removeAll(permittedAttrs); System.out.println("Illegal attributes: " + illegal); valid = false; } return valid; }
假設(shè)你想知道兩個Map對象共有的所有鍵。
SetcommonKeys = new HashSet (m1.keySet()); commonKeys.retainAll(m2.keySet());
類似的語法可以為你提供共同的值。
到目前為止提出的所有語法都是非破壞性的,也就是說,它們不會修改支持Map,這里有一些,假設(shè)你要刪除一個Map與另一個Map共有的所有鍵值對。
m1.entrySet().removeAll(m2.entrySet());
假設(shè)你要從一個Map中刪除在另一個Map中具有映射的所有鍵。
m1.keySet().removeAll(m2.keySet());
在同一個批量操作中開始混合鍵和值時會發(fā)生什么?假設(shè)你有一個Map,managers,將公司中的每個員工映射到員工的經(jīng)理,我們會故意模糊鍵和值對象的類型,沒關(guān)系,只要它們是相同的,現(xiàn)在假設(shè)你想知道所有“個人貢獻者”(或非管理者)是誰,以下代碼段將準(zhǔn)確告訴你你想要了解的內(nèi)容。
SetindividualContributors = new HashSet (managers.keySet()); individualContributors.removeAll(managers.values());
假設(shè)你要解雇所有直接向某位經(jīng)理Simon報告的員工。
Employee simon = ... ; managers.values().removeAll(Collections.singleton(simon));
請注意,這個語法是使用Collections.singleton,這是一個靜態(tài)工廠方法,它返回一個帶有指定元素的不可變Set。
一旦你完成了這項工作,你可能會有一群員工,他們的經(jīng)理不再為公司工作(如果任何Simon的直接報告本身就是經(jīng)理),以下代碼將告訴你哪些員工擁有不再為公司工作的經(jīng)理。
Mapm = new HashMap (managers); m.values().removeAll(managers.keySet()); Set slackers = m.keySet();
這個例子有點棘手,首先,它創(chuàng)建Map的臨時副本,并從臨時副本中刪除其(manager)值是原始Map中的鍵的所有條目,請記住,原始Map為每個員工都有一個條目。因此,臨時Map中的其余條目包括來自原始Map的其(經(jīng)理)值不再是雇員的所有條目,因此,臨時副本中的鍵恰好代表了我們正在尋找的員工。
多重映射多重映射就像Map,但它可以將每個鍵映射到多個值,Java集合框架不包含多重映射的接口,因為它們并不常用。使用Map值為List實例作為多重映射的Map是一件相當(dāng)簡單的事情。下一個代碼示例演示了此技術(shù),該示例讀取每行包含一個單詞(全部小寫)的單詞列表,并打印出符合大小標(biāo)準(zhǔn)的所有變位詞組。變位詞組是一堆單詞,所有單詞都包含完全相同的字母,但順序不同,該程序在命令行上有兩個參數(shù):(1)字典文件的名稱,(2)要打印出的變位詞組的最小尺寸,不打印包含少于指定最小值的單詞組的變位詞組。
找到變位詞組有一個標(biāo)準(zhǔn)技巧:對于字典中的每個單詞,按字母順序排列單詞中的字母(即,將單詞的字母重新排序為字母順序)并將條目放入多重映射,將字母順序排列的單詞映射到原始單詞。例如,單詞bad導(dǎo)致將abd條目映射為bad以將其放入多重映射中,稍作思考就會發(fā)現(xiàn),任何給定鍵映射到的所有單詞都構(gòu)成一個變位詞組。迭代多重映射中的鍵,打印出符合大小約束的每個變位詞組是一件簡單的事情。
以下程序是該技術(shù)的直接實現(xiàn)。
import java.util.*; import java.io.*; public class Anagrams { public static void main(String[] args) { int minGroupSize = Integer.parseInt(args[1]); // Read words from file and put into a simulated multimap Map> m = new HashMap >(); try { Scanner s = new Scanner(new File(args[0])); while (s.hasNext()) { String word = s.next(); String alpha = alphabetize(word); List l = m.get(alpha); if (l == null) m.put(alpha, l=new ArrayList ()); l.add(word); } } catch (IOException e) { System.err.println(e); System.exit(1); } // Print all permutation groups above size threshold for (List l : m.values()) if (l.size() >= minGroupSize) System.out.println(l.size() + ": " + l); } private static String alphabetize(String s) { char[] a = s.toCharArray(); Arrays.sort(a); return new String(a); } }
在173,000字的字典文件上運行此程序,最小變位詞組大小為8會產(chǎn)生以下輸出。
9: [estrin, inerts, insert, inters, niters, nitres, sinter, triens, trines] 8: [lapse, leaps, pales, peals, pleas, salep, sepal, spale] 8: [aspers, parses, passer, prases, repass, spares, sparse, spears] 10: [least, setal, slate, stale, steal, stela, taels, tales, teals, tesla] 8: [enters, nester, renest, rentes, resent, tenser, ternes, treens] 8: [arles, earls, lares, laser, lears, rales, reals, seral] 8: [earings, erasing, gainers, reagins, regains, reginas, searing, seringa] 8: [peris, piers, pries, prise, ripes, speir, spier, spire] 12: [apers, apres, asper, pares, parse, pears, prase, presa, rapes, reaps, spare, spear] 11: [alerts, alters, artels, estral, laster, ratels, salter, slater, staler, stelar, talers] 9: [capers, crapes, escarp, pacers, parsec, recaps, scrape, secpar, spacer] 9: [palest, palets, pastel, petals, plates, pleats, septal, staple, tepals] 9: [anestri, antsier, nastier, ratines, retains, retinas, retsina, stainer, stearin] 8: [ates, east, eats, etas, sate, seat, seta, teas] 8: [carets, cartes, caster, caters, crates, reacts, recast, traces]
許多這些詞似乎有點虛偽,但這不是程序的錯;它們在字典文件中,這是使用的字典文件,它源自Public Domain ENABLE基準(zhǔn)參考詞列表。
上一篇:Deque接口 下一篇:對象排序文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/73946.html
集合接口 核心集合接口封裝了不同類型的集合,如下圖所示,這些接口允許獨立于其表示的細節(jié)來操縱集合,核心集合接口是Java集合框架的基礎(chǔ),如下圖所示,核心集合接口形成層次結(jié)構(gòu)。 showImg(https://segmentfault.com/img/bVbntJW?w=402&h=146); Set是一種特殊的Collection,SortedSet是一種特殊的Set,依此類推,另請注意,層次結(jié)構(gòu)...
摘要:簡明教程原文譯者黃小非來源簡明教程并沒有沒落,人們很快就會發(fā)現(xiàn)這一點歡迎閱讀我編寫的介紹。編譯器會自動地選擇合適的構(gòu)造函數(shù)來匹配函數(shù)的簽名,并選擇正確的構(gòu)造函數(shù)形式。 Java 8 簡明教程 原文:Java 8 Tutorial 譯者:ImportNew.com - 黃小非 來源:Java 8簡明教程 ? Java并沒有沒落,人們很快就會發(fā)現(xiàn)這一點 歡迎閱讀我編寫的Java ...
摘要:并發(fā)教程原子變量和原文譯者飛龍協(xié)議歡迎閱讀我的多線程編程系列教程的第三部分。如果你能夠在多線程中同時且安全地執(zhí)行某個操作,而不需要關(guān)鍵字或上一章中的鎖,那么這個操作就是原子的。當(dāng)多線程的更新比讀取更頻繁時,這個類通常比原子數(shù)值類性能更好。 Java 8 并發(fā)教程:原子變量和 ConcurrentMap 原文:Java 8 Concurrency Tutorial: Synchroni...
SortedMap接口 SortedMap是一個按升序維護其條目的Map,根據(jù)鍵的自然順序或在創(chuàng)建SortedMap時提供的Comparator進行排序,SortedMap接口提供常規(guī)Map操作和以下操作的操作: 范圍視圖 — 對排序后的map執(zhí)行任意范圍操作 端點 — 返回已排序map中的第一個或最后一個鍵 比較器訪問 — 返回用于排序map的Comparator(如果有的話) 下面的接口是...
抽象方法和類 抽象類是一個聲明為abstract的類 — 它可能包括也可能不包括抽象方法,抽象類無法實例化,但可以進行子類化。 抽象方法是在沒有實現(xiàn)的情況下聲明的方法(沒有大括號,后跟分號),如下所示: abstract void moveTo(double deltaX, double deltaY); 如果一個類包含抽象方法,那么該類本身必須被聲明為abstract,如: public abs...
閱讀 5055·2021-11-25 09:43
閱讀 1695·2021-10-27 14:18
閱讀 1063·2021-09-22 16:03
閱讀 1356·2019-08-30 13:19
閱讀 1580·2019-08-30 11:15
閱讀 1649·2019-08-26 14:04
閱讀 3129·2019-08-23 18:40
閱讀 1172·2019-08-23 18:17