Java 中的map - The Map Interface.
簡介
Map是一個鍵對映到值的物件。 一個Map不能包含任何的重複的鍵,也就是說每個鍵最多對映到一個值。他模擬了數學概念中的對映。Map 介面中包括了基本的操作(put,get,remove etc)和 多元操作 (putall and clear等)還有 集合檢視(keyset 等)
Java 平臺上包含了三種主要的Map 介面的實現。1. Hashmap 2. TreeMap 3. LinkedHashMap。 他們的特徵和效能正是和Hashset, Treeset, LinkedHashSet 類似。
以下的內容讓我們來看看Map 介面的細節。但是首先,這裡有幾個關於JDK 8 Map操作的例子。模擬真實世界的物件是物件導向程式設計的一個常見的任務,所以讓我們來找一個合理的真實世界的例子。 想象下在某個部門有一組員工。
// Group employees by department
Map<Department, List<Employee>> byDept = employees.stream()
.collect(Collectors.groupingBy(Employee::getDepartment));
或者計算某個部門所有員工的工資總和
// Compute sum of salaries by department
Map<Department, Integer> totalByDept = employees.stream()
.collect(Collectors.groupingBy(Employee::getDepartment,
Collectors.summingInt(Employee::getSalary)));
我們還可以通過city分類people
// Classify Person objects by city
Map<String, List<Person>> peopleByCity
= personStream.collect(Collectors.groupingBy(Person::getCity));
或者更復雜點的map
// Cascade Collectors
Map<String, Map<String, List<Person>>> peopleByStateAndCity
= personStream.collect(Collectors.groupingBy(Person::getState,
Collectors.groupingBy(Person::getCity)))
(這裡是我自己的: 在java 8中新引用的stream(), 給我門的程式設計提供了很多的便利,stream 簡單來說就是可以講collection<T>轉化成一個T的stream然後對這個stream應用一些方法 例如filter or map etc詳情請見Stream API)
Map 介面的基本操作
put
, get
, containsKey
, containsValue
, size
,
and isEmpty
) 他們的表現是和hashtable中是一樣的。以下這段程式碼顯示的是找到輸入list中各個單詞出現的頻數。import java.util.*;
public class Freq {
public static void main(String[] args) {
Map<String, Integer> m = new HashMap<String, Integer>();
// 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);
}
}
以上程式碼只有一點稍微有點意思就是 map的put操作。他的引數是一個條件表示式,因為他會先判斷這個被操作的單詞是否已經在map中,如果在的話就講當前的頻數加1.
if it is to be it is up to me to delegate
那麼輸入將會是8 distinct words:
{to=3, delegate=1, be=1, it=2, up=1, if=1, me=1, is=2}
8 distinct words:
{be=1, delegate=1, if=1, is=2, it=2, me=1, to=3, up=1}
但是如果你想要得到的結果是按照輸入的順序的話(單詞第一次出現位置的順序),那麼則需要把HashMap換成LinkedHashMap
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從而可以忽略他們的實現型別簡單來說就是如果兩個map的鍵值對是一樣的那麼這兩個map就是一樣的。
Map<K, V> copy = new HashMap<K, V>(m);
Map 介面的Bulk操作
clear這個操作的作用和他的字面意思是一樣的。它將刪掉map上所有的mapping關係。putall操作和collection介面的addall操作很像。而且,很明顯的用途是將一個map完全放入另一個map中。他還有另一個更靈活的用法。假設有一個map被用來表示一個attribute-value對的集合;putall操作加上map的建構函式一起就能提供一個乾淨利索的方法來實現例項化並將default的賦給他自己。
(自己的理解: 他這一大段就說了一個事,想要把兩個map給放到一起我們可以使用putall方法, 值得注意的是如果兩個map有相同key的話下面程式碼的override的那個map會佔主導地位)
static <K, V> Map<K, V> newAttributeMap(Map<K, V>defaults, Map<K, V> overrides) {
Map<K, V> result = new HashMap<K, V>(defaults);
result.putAll(overrides);
return result;
}
集合檢視
for (KeyType key : m.keySet())
System.out.println(key);
// Filter a map based on some
// property of its keys.
for (Iterator<Type> it = m.keySet().iterator(); it.hasNext(); )
if (it.next().isBogus())
it.remove();
迭代map的值的方法和這個方法很類似,如下
for (Map.Entry<KeyType, ValType> e : m.entrySet())
System.out.println(e.getKey() + ": " + e.getValue());
很多人都會擔心這種方法來迭代一個map可能很慢,因為map必須建立一個新的Collection 物件當每次Collection 檢視的操作被呼叫。
呼叫 Iterator 的remove 方法再配合使用collection view的三種方式,我們能對map中的元素進行刪除操作。 這個就是前面的Iterator的例子
通過entrySet檢視,給了我們一種能改變某個鍵的值的可能性。Map.entry的setvalue方法在迭代中可以對entry的值進行更改。注意這是唯一的一種安全的在迭代中改變map的值的方法。如果底層map在迭代中被另一種方式修改那麼這個行為將是未指定的。
集合檢視支援多種元素刪除的方式 - remove, removeall, retainAll(保持輸入set刪掉剩餘其他), 和 clear. 和 Iterator.Remove的操作。
集合檢視在某些情況下不支援元素的新增。因為他不是很符合常理當我們使用KeySet 和 values view的時候進行新增操作。同理對於entryset也是一樣的。因為map的本身就提供了put 和 putAll的操作。
(自己的觀點:以上這一小節說的挺繞的,但是其實就是說Collection view例如 keyset values entryset等等在某些情況下有某些作用,並且把他們和Map這個概念分開來講。我是這樣理解的。)
Collection 檢視的一些很厲害的用法
if (m1.entrySet().containsAll(m2.entrySet())) {
...
}
相似的,如果你想要知道是否兩個map的key是否是一樣的那麼可以使用
if (m1.keySet().equals(m2.keySet())) {
...
}
static <K, V> boolean validate(Map<K, V> attrMap, Set<K> requiredAttrs, Set<K>permittedAttrs) {
boolean valid = true;
Set<K> attrs = attrMap.keySet();
if (! attrs.containsAll(requiredAttrs)) {
Set<K> missing = new HashSet<K>(requiredAttrs);
missing.removeAll(attrs);
System.out.println("Missing attributes: " + missing);
valid = false;
}
if (! permittedAttrs.containsAll(attrs)) {
Set<K> illegal = new HashSet<K>(attrs);
illegal.removeAll(permittedAttrs);
System.out.println("Illegal attributes: " + illegal);
valid = false;
}
return valid;
}
Set<KeyType>commonKeys = new HashSet<KeyType>(m1.keySet());
commonKeys.retainAll(m2.keySet());
m1.entrySet().removeAll(m2.entrySet());
假設你想要從map1中移除所有map2中有的鍵,你這樣這樣
m1.keySet().removeAll(m2.keySet());
Set<Employee> individualContributors = new HashSet<Employee>(managers.keySet());
individualContributors.removeAll(managers.values());
Employee simon = ... ;
managers.values().removeAll(Collections.singleton(simon));
當 你做完下面這些操作之後,你可能就會發現有哪些員工的manager已經不在為這個公司工作了,最大的manager按照給他自己彙報的原則來算。(這個例子還挺好玩大家好好感受感受)
Map<Employee, Employee> m = new HashMap<Employee, Employee>(managers);
m.values().removeAll(managers.keySet());
Set<Employee> slackers = m.keySet();
Multimaps 多重map
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<String, List<String>> m = new HashMap<String, List<String>>();
try {
Scanner s = new Scanner(new File(args[0]));
while (s.hasNext()) {
String word = s.next();
String alpha = alphabetize(word);
List<String> l = m.get(alpha);
if (l == null)
m.put(alpha, l=new ArrayList<String>());
l.add(word);
}
} catch (IOException e) {
System.err.println(e);
System.exit(1);
}
// Print all permutation groups above size threshold
for (List<String> 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);
}
}
如下是輸出的結果
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]
這裡有 字典檔案的樣本。相關文章
- java中的Map集合Java
- Java中將多個Map扁平化為單個MapJava
- Java中實現不可變MapJava
- Go中的MapGo
- Java中的Map集合學習筆記Java筆記
- java中遍歷map的集中方法Java
- Java 中的泛型 集合(List,Set) MapJava泛型
- Java map轉JSONJavaJSON
- Java集合類——MapJava
- JAVA集合——Map介面Java
- Java筆記——【Map】Java筆記
- 深入淺出java的MapJava
- Java 8 中 Map 騷操作之 merge() 的用法Java
- 【Java中遍歷Map物件的4種方法】Java物件
- 【java】【Map】HashMap、Hashtable、CollectionsJavaHashMap
- JAVA集合框架 - Map介面Java框架
- Java Map集合練習Java
- Java中將 Map 轉換為 Spring MultiValueMapJavaSpring
- Java Map的最佳實踐 - tremblayJavaREM
- Java遍歷Map集合的方法Java
- Map中取值toString()
- Java中如何遍歷Map物件的4種方法Java物件
- stl中map的基本用法
- ES6中的Map
- java Map相關總結Java
- Java物件轉換成MapJava物件
- Java集合四:Map簡介;Java
- Java : List中 根據map的某個key去重Java
- 聊一聊Java8中map的putIfAbsent,computeIfAbsent 方法Java
- jquery 中 $.map 的使用方法jQuery
- 詳解object detection中的mAPObject
- GO 中 map 的實現原理Go
- Map
- Map集合&&Map集合的不同遍歷【keySet()&&entrySet()】
- 如何在Java 8中將List轉換為Map?Java
- Java中List集合轉Map集合報錯:Duplicate keyJava
- Java遍歷Map物件的四種方式Java物件
- Java交換map的key和value值Java
- Map集合(Java基礎、skycto JEEditor)Java