简介
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]
这里有
字典文件的样本。