`
huang5787826
  • 浏览: 45904 次
  • 性别: Icon_minigender_1
  • 来自: 厦门
文章分类
社区版块
存档分类
最新评论

java map的超大用处...

阅读更多
Java Map 集合类简介请参考
Map接口  
  Map是一个将键映射为值的对象。一个映射不能包含重复键:每个键最多能映射一个值。Map接口如下所示:  
 
public interface Map {
// Basic Operations
Object put(Object key, Object value);
Object get(Object key);
Object remove(Object key);
boolean containsKey(Object key);
boolean containsValue(Object value);
int size();
boolean isEmpty(); 
 
// Bulk Operations
void putAll(Map t);
void clear(); 
 
// Collection Views
public Set keySet();
public Collection values();
public Set entrySet(); 

// Interface for entrySet element
public interface Entry {
Object getKey();
Object getValue();
Object setValue(Object value);
}
} 
 

  JDK包含两个新的通用Map实现,一个是HashMap, 它将它的项存储在一个哈希表中,是一种最好的实现;另一个是TreeMap, 它将它的项存储在一个红-黑树上,它可保证迭代的顺序。 另外, Hashtable已被改进以实现Map。

  与哈希表的比较  

  如果你使用过Hashtable, 你应该已经熟悉了Map的一般风格(当然Map是一个接口,而Hashtable是一个具体的实现)。 以下是它们的主要区别:

  Map提供Collection视图,作为Enumeration对象的替代直接支持迭代过程。Collection视图 极大地提高了接口的可表达性,正如后续课程将讲到的。  
 
  Map允许你在键、值或键-值对上进行迭代;Hashtable则不提供第三个选项。  
 
  Map提供了在迭代过程中删除项的安全途径;Hashtable则不能。

  进一步讲,Map修补了Hashtable接口上的某些小缺陷。 Hashtable具有一个称作contains的方法,如果Hashtable包含一个给定值,它将返回true。 从它的名字上理解, 你可能期望如果Hashtable包含一个给定的key, 这个方法也会返回一个true ,因为键是一个Hashtable的主要存取机制。 Map接口通过将这个方法重新命名为containsValue,从而消除了引起混乱的来源;同时也改善了接口的一致性: containsValue与containsKey可很好地对应并行。  
 
  基本操作  
 
  基本操作 (put, get, remove, containsKey, containsValue, s , a和isEmpty) 的功能与它们在Hashtable中的对等物非常相似。下面的简单程序针对参数列表中的词汇生成一个频率表。频率表将每个词和它在参数列表中所出现的次数相映射。  
 
import java.util.*;
public class Freq { private static final Integer ONE = new Integer(1);
public static void main(String args[]) {
Map m = new HashMap();
// Initialize frequency table from command line
for (int i=0; i$#@60; args.length; i++) {
Integer freq = (Integer) m.get(args[i]);
m.put(args[i], (freq==null ? ONE :
new Integer(freq.intValue() + 1)));
} 
 
System.out.println(m.size()+" distinct words detected:");
System.out.println(m);
}
}
 

  有关这个程序的一个小技巧是put语句的第二个参数。这是一个条件表达式,它的效果是如果这个词汇以前从未被看到过,则将频率设置为one,如果这个词汇已经被看到,则设置为当前值加1 。让我们来运行这个程序:  
 
% java Freq if it is to be it is up to me to delegate
8 distinct words detected:
{to=3, me=1, delegate=1, it=2, is=2, if=1, be=1, up=1} 
 
  假设你更喜欢以字母顺序排列的频率表,那么所有你要做的工作就是将Map的实现类型从HashMap改变为TreeMap. 这四个字母的改变,使该程序从相同的命令行生成了如下输出:  
 
8 distinct words detected:
{be=1, delegate=1, if=1, is=2, it=2, me=1, to=3, up=1} 
 
  这个接口"酷"吗?怎么样?  
 
  正如Set和List接口一样, Map加强了对equals和hashCode方法的要求, 于是, 两个Map对象可做逻辑等同性比较而不必考虑它们的实现类型。 如果它们显示了相等的键-值映射, 则两个Map对象是相等的。  
 
  按惯例, 所有的Map实现可提供构造函数; 该构造函数提取一个Map对象并将这个新的Map初始化, 使之包含特定Map中的所有键-值映射。这个标准Map构造函数是为Collection实现而设计的标准对象集 构造函数的完全对等物。它允许调用者创建一个期望的实现类型的Map ; 该实现类型初始包含另一个Map的所有映射。 而不考虑其它Map的实现类型。例如,假设你有一个命名为m的Map, 则下列一行代码创建了一个新的HashMap, 它初始包含所有与m相同的键-值映射:  
 
  Map copy = new HashMap(m);

  批量操作(Bulk Operations)  
 

clear操作所完成的工作正象其词义上所表达的那样: 它从Map 中删除所有映射。putAll操作是Collection接口中的addAll操作的Map对等物; 它可将一个Map转储至另一个, 除此之外, 它还有一个更微妙的用处。假设一个Map被用来表示属性-值对(attribute-value pairs ) ; putAll操作将与标准Map构造函数一起提供一种用默认值创建属性表的简捷方法。以下是一个可演示此种技术的静态方法:  
 
static Map newAttributeMap(Map defaults, Map overrides) {
Map result = new HashMap(defaults);
result.putAll(overrides);
return result;
} 
 

 
  Collection视图  
 
  Collection视图 方法允许以三种方式将一个Map作为一个Collection来视图:

  keySet: 包含在Map中的键的Set。  
 
  values: 包含在Map中的值的Collection。 该Collection不是一个Set, 因为多个键可映射相同的值。
 
  entrySet: 包含在Map中的键-值对的Set 。Map接口提供了一个小的被称作Map.Entry的嵌套接口,它是在这个Set中的元素的类型。  
 
   Collection视图提供了在Map上进行迭代的唯一方法。下面的例子给出了在一个Map上迭代键的标准惯用程序:  
 
for (Iterator i=m.keySet().iterator(); i.hasNext(); )
System.out.println(i.next());

  对值进行迭代的惯用程序是类似的。这是迭代键-值对的惯用程序:

for (Iterator i=m.entrySet().iterator(); i.hasNext(); ) {
Map.Entry e = (Map.Entry) i.next();
System.out.println(e.getKey() + ": " + e.getValue());
}
 


  第一次提交这些惯用程序时,许多人考虑到每次调用一个Collection视图时,Map都必须创建一个新的Collection对象,因而担心其速度慢。请放心:这是不可能的。如果每次一个Map被要求给出一个特定的Collection视图时,没有道理Map不能总是返回相同的对象。这恰恰是所有JDK的Map实现所要作的事。  
 
  用所有三个Collection视图, 调用一个Iterator的remove的操作可从后备Map中删除相关项(假设该Map支持删除)。用entrySet视图, 通过在迭代过程中调用一个Map.Entry的setValue方法 (再一次假设该Map支持值的更改),也可能改变与一个键相关的值。 请注意这是在迭代过程中更改一个Map的唯一安全途径。  
 
  Collection视图支持它的所有形式的元素删除:remove, removeAll, retainAll, 和clear操作, 以及Iterator.remove操作 (然而,这是建立在假设后备Map支持元素删除的基础之上)。  
 
  Collection视图在任何情况下都不支持元素增加。对keySet和values视图这是无意义的,而对entrySet视 
  Collection视图的奇特用法: Map代数  
 
  在应用Collection视图时,批量操作 (containsAll, removeAll和retainAll) 是一个惊人的有力工具。假设你要了解一个Map是否是另一个的子映射(submap),也就是说,第一个Map是否包含第二个的全部键-值映射,请看下面惯用程序的小技巧:  
 
if (m1.entrySet().containsAll(m2.entrySet())) {
..
}

  对照类似行,假设你要了解两个Map对象是否包含所有所有相同键的映射

if (m1.keySet().equals(m2.keySet())) {
...
} 
图来说,这是没必要的,因为后备Map的put和putAll提供了相同的功能。

  假设你具有一个映射,代表一个属性-值对集合;以及两个sets, 表示要求的属性和允许的属性(允许的属性包括要求的属性)。下列代码可判定该属性映射是否符合那些限定条件,如果不符合,则打印详细的出错消息:  
 
boolean valid = true;
Set attributes = attributeMap.keySet();
if(!attributes.containsAll(requiredAttributes)) {
Set missing = new HashSet(requiredAttributes);
missing.removeAll(attributes);
System.out.println("Missing required attributes: "+missing);
valid = false;
} 
 
if (!permissibleAttributes.containsAll(attributes)) {
Set illegal = new HashSet(attributes);
illegal.removeAll(permissibleAttributes);
System.out.println("Contains illegal attributes: "+illegal);
valid = false;
} 
 
if (valid)
System.out.println("OK");

 

  假设你想了解由两个Map对象公用的所有键:

Set commonKeys = new HashSet(a.keySet());
commonKeys.retainAll(b.keySet()); 
 
  类似的惯用程序使你可以获得公共值以及公共键-值对。要获得公共键-值对,则需格外小心; 因为结果Set的元素(即Map.Entry对象)在Map被更改后,可能是无效的。 到目前为止,所有惯用程序都是"非破坏性的":它们不更改后备Map。 下面是一些更改后备Map的例子。假设你要删除一个Map与另一个Map所共有的所有键-值对:  
 
m1.entrySet().removeAll(m2.entrySet());



  假设你要从一个Map中删除所有在另一个Map中具有映射的键:  
 
m1.keySet().removeAll(m2.keySet());  
 
  当你在同样的批量操作中开始混合键和值时,发生了什么事情呢?假设你有一个称作managers的Map, 它将公司中的每个雇员与该雇员的经理相映射。我们对键和值对象的类型是不清楚的。这不要紧, 只要它们是相同的类型就可以了。现在,假设你要知道全部的"个体贡献者"是谁? (这是为不是经理的雇员所用的公司语言)。下面的一行程序准确地告诉你所要了解的东西:  
 
Set individualContributors = new HashSet(managers.keySet());
individualContributors.removeAll(managers.values()); 
 

 
  假设你要辞退直接向某些经理报告的雇员(我们称他为herbert):
Employee herbert = ... ;
managers.values().removeAll(Collections.singleton(herbert)); 
 

 
  请注意,这个惯用程序利用了Collections.singleton, 它是一个静态方法,可返回一个永恒的带有单一特定元素的Set。  

  一旦完成了这些工作,你就可能有了一帮雇员,他们的经理不再为公司工作(如果任何herbert的直接报告是他们自己的经理)。下列代码告诉你所有的他们的经理不再为公司工作的雇员:  
 
Map m = new HashMap(managers);
m.values().removeAll(managers.keySet());
Set slackers = m.keySet();
 
 
  这个例子是一个小的技巧。首先,它作了一个Map的临时拷贝,然后又从这个临时拷贝中删除所有的经理值是初始Map中的键的项。记住这个初始Map包含一个为每一个雇员准备的项。于是,在临时Map中保留的项包含了初始Map中的经理值不再是雇员。在临时拷贝中的键则恰恰表示了我们正在寻找的雇员的所有项。如果你把这个例子多看看,你就应该全清楚了。如果还不清楚,该去拿一杯热气腾腾刚酿好的Java饮料了。

  还有许多与本章中的惯用程序类似的例子,但要把它们全列出来则过于烦琐,也是不实际的,一旦你掌握了它的用法,你就很容易在你需要它的时候拿出正确的解决方案。多重映射(Multimaps)一个multimap与一个map类似, 只是它可以将每个键映射为多个值。Collections Framework不包括多重映射接口,因为它们不是很普遍地被使用。将一个其值为List对象的Map当作多重映射来使用则是相当简单的事情。这个技术在下一个代码举例中将被演示,这个例子是阅读每行(全部小写)一个单词的一部词典并打印所有满足尺寸标准的permutation groups(排列组)。 一个排列组是一组单词,它们包含完全相同的字母,但字母顺序不同。这个程序在命令行中使用了两个参数:词典文件名和要打印的排列组的尺寸;排列组包含的单词如果少于指定的最小值,则该排列组不被打印。  

  有一个查找排列组的标准技巧:将词典中的每个词的字母按字母顺序进行排列(即将一个词的字母按字母顺序记录下来)并将一个项放入一个多重映射,将经过字母排序的单词映射到原来的单词。例如,单词"bad" 导致一个项映射 "abd" 至 "bad" 被放入多重映射。一个瞬间的反射将显示任何给定的键映射到所有单词形成一个排列组。在一个多重映射中迭代键以及打印满足尺寸条件的每一个排列组是一个简单的事情。  
 
  下列程序是这个技术的一个直接的实现。仅有的技巧部分是alphabetize方法,它返回一个串,这个串包含与它的参数相同的字符,并按字母顺序排列。这个例程(它与Collections Framework无关) 实现一个巧妙的桶式分类。 它假定按字母顺序排列的单词完全由小写字母组成。

import java.util.*;
import java.io.*; 
 
public class Perm {
public static void main(String[] args) {
int minGroupSize = Integer.parseInt(args[1]); 
 
// Read words from file and put into simulated multimap
Map m = new HashMap();
try {
BufferedReader in =
new BufferedReader(new FileReader(args[0]));
String word;
while((word = in.readLine()) != null) {
String alpha = alphabetize(word);
List l = (List) 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 (Iterator i = m.values().iterator(); i.hasNext(); ) {
List l = (List) i.next();
if (l.size() $#@62; = minGroupSize)
System.out.println(l.size() + ": " + l);
}
} 
 
private static String alphabetize(String s) {
int count[] = new int[256];
int len = s.length();
for (int i=0; i$#@60; len; i++)
count[s.charAt(i)]++;
StringBuffer result = new StringBuffer(len);
for (char c="a"; c$#@60; ="z"; c++)
for (int i=0; i$#@60; count[c]; i++)
result.append(c);
return result.toString();
}
}
 



  在一台老式UltraSparc 1上运行一个有80,000个单词的词典用了大约16秒;最小排列组尺寸为8 。它生成了下列输出  
 
% java Perm dictionary.txt 8
9: [estrin, inerts, insert, inters, niters, nitres, sinter,
triens, trines]
8: [carets, cartes, caster, caters, crates, reacts, recast, traces]
9: [capers, crapes, escarp, pacers, parsec, recaps, scrape, secpar,
spacer]
8: [ates, east, eats, etas, sate, seat, seta, teas]
12: [apers, apres, asper, pares, parse, pears, prase, presa, rapes,
reaps, spare, spear]
9: [anestri, antsier, nastier, ratines, retains, retinas, retsina,
stainer, stearin]
10: [least, setal, slate, stale, steal, stela, taels, tales, teals,
tesla]
8: [arles, earls, lares, laser, lears, rales, reals, seral]
8: [lapse, leaps, pales, peals, pleas, salep, sepal, spale]
8: [aspers, parses, passer, prases, repass, spares, sparse, spears]
8: [earings, erasing, gainers, reagins, regains, reginas, searing,
seringa]
11: [alerts, alters, artels, estral, laster, ratels, salter, slater,
staler, stelar, talers]
9: [palest, palets, pastel, petals, plates, pleats, septal, staple,
tepals]
8: [enters, nester, renest, rentes, resent, tenser, ternes, treens]
8: [peris, piers, pries, prise, ripes, speir, spier, spire] 
 
  对象排序  

  一个 List l 可能被做如下排序:  
 
Collections.sort(l);  
 
  如果这个 list 由 String 元素所组成, 那么它将按词典排序法(按字母顺序)进行排序; 如果它是由 Date 元素所组成, 那么它将按年代顺序来排序。 Java 怎么会知道该怎么做呢? 这一定是个魔术! 其实不然。实际上, String 和 Date 均实现了Comparable接口。 Comparable 接口为一个类提供一个 自然排序( natural ordering), 它允许那个类的对象被自动排序。下表列出了实现了Comparable 的JDK类:


  类 自然排序

  Byte 带符号的数字排序

  Character 不带符号的数字排序

  Long 带符号的数字排序

  Integer 带符号的数字排序

  Short 带符号的数字排序

  Double 带符号的数字排序

  Float 带符号的数字排序

  BigInteger 带符号的数字排序

  BigDecimal 带符号的数字排序

  File 依赖系统的按路径名字母顺序排序

  String 按字母顺序排序

  Date 按年代顺序排序

  CollationKey 特定字符集按字母顺序排序 
    
  如果你要为一个其元素没有实现 Comparable的列表排序,Collections.sort(list) 将扔出一个 ClassCastException。类似的,如果你要为一个其元素没有作相互比较的列表进行排序, Collections.sort 将扔出一个 ClassCastException. 能够被相互比较的元素被称作 mutually comparable(可相互比较的)。 虽然不同类型的元素有可能被相互比较,但以上列出的任何JDK类型都不允许在类之间的比较 (inter-class comparison)。  
 
  如果你只是要为可比较的元素的列表进行排序,或为它们创建排序的对象集, 则这就是你实际需要了解的全部有关 Comparable 接口的内容。如果你要实现你自己的 Comparable 类型,则下一节将会引起你的兴趣。

分享到:
评论

相关推荐

    Java 面试宝典

    Java 基础部分..................................................................................................................... 7 1、一个".java"源文件中是否可以包括多个类(不是内部类)?有什么...

    经典JAVA.EE企业应用实战.基于WEBLOGIC_JBOSS的JSF_EJB3_JPA整合开发.pdf

    中文名: 经典Java EE企业应用实战--基于WebLogic/JBoss的JSF+EJB 3+JPA整合开发 原名: 经典Java EE企业应用实战--基于WebLogic/JBoss的JSF+EJB 3+JPA整合开发 作者: 李刚 资源格式: PDF 版本: 第一版 出版社: 电子...

    java面试题

    24. List, Set, Map区别 14 25. 集合类都有哪些?主要方法? 14 26. 简述逻辑操作(&,|,^)与条件操作(&&,||)的区别。 14 27. XML文档定义有几种形式?它们之间有何本质区别?解析XML文档有哪几种方式? 14 28. JSP和...

    Java高级程序设计:第7章-集合框架.pptx

    根据不同类型的集合的特点和用途,集合框架在设计的时候将集合分为以下三种类型: (1) 集合(Set) — 无重复对象 (2) 列表(List) — 允许重复对象,按索引检索,类似 数组 (3) 映射(Map) — 每个元素由key对象和value...

    java面试宝典

    java面试试题 全面 准确 带答案 coreJava部分 8 1、面向对象的特征有哪些方面? 8 2、作用域public,private,protected,以及不写时的区别? 8 3、String 是最基本的数据类型吗? 8 4、float 型float f=3.4是否正确? 8 ...

    java 面试题 总结

    Hashtable继承自Dictionary类,而HashMap是Java1.2引进的Map interface的一个实现。 最大的不同是,Hashtable的方法是Synchronize的,而HashMap不是,在多个线程访问Hashtable时,不需要自己为它的方法实现同步,而...

    java基础题 很全面

    用途是什么? 29 11. Spring transaction properties 29 编程/代码 30 1. 编程题: 用最有效率的方法算出2乘以8等於几? 30 2. 我们在web应用开发过程中经常遇到输出某种编码的字符,如iso8859-1等,如何输出一个某种编码...

    JAVA基础课程讲义

    Map接口 138 Iterator接口 139 遍历集合 140 Collections工具类 141 Comparable接口 141 equals和hashcode方法 143  泛型 144 思考作业 145 上机作业 145 第八章 IO技术 146 为什么需要学习IO技术 146 基本概念 ...

    Java面试题,冲冲冲!.rar

    List、Set、Queue和Map是Java集合框架中的四个主要接口,它们各自具有不同的特点和用途。 1. List(列表): - 允许重复元素。 - 具有按照元素插入顺序维护的有序集合。 - 可以通过索引访问和操作元素。 - 常见实现类...

    Java面试宝典-经典

    2、编写一个程序,将d:\java目录下的所有.java文件复制到d:\jad目录下,并将原来文件的扩展名从.java改为.jad。 62 3、编写一个截取字符串的函数,输入为一个字符串和字节数,输出为按字节截取的字符串,但要保证...

    TiledMapInfoExport:将平铺的地图编辑器(TMX)文件信息导出到json文件

    方便其他工具读取对应的自定义属性使用方法java -jar TilemapInfoExport.jar **TileMapPath** **ExportPath**like:java -jar TilemapInfoExport.jar C:\tilemap01.tmx E:\out\output.jsonOverviewTile

    java面试题大全(2012版)

    2、编写一个程序,将d:\java目录下的所有.java文件复制到d:\jad目录下,并将原来文件的扩展名从.java改为.jad。 62 3、编写一个截取字符串的函数,输入为一个字符串和字节数,输出为按字节截取的字符串,但要保证...

    最新Java面试宝典pdf版

    2、编写一个程序,将d:\java目录下的所有.java文件复制到d:\jad目录下,并将原来文件的扩展名从.java改为.jad。 62 3、编写一个截取字符串的函数,输入为一个字符串和字节数,输出为按字节截取的字符串,但要保证...

    Java面试笔试资料大全

    2、编写一个程序,将d:\java目录下的所有.java文件复制到d:\jad目录下,并将原来文件的扩展名从.java改为.jad。 62 3、编写一个截取字符串的函数,输入为一个字符串和字节数,输出为按字节截取的字符串,但要保证...

    java面试宝典2012

    各种java面试题集,面试前必备哦, 1. Java基础部分 7 1、一个".java"源文件中是否可以包括多个类(不是内部类)?有什么限制? 8 2、Java有没有goto? 8 3、说说&和&&的区别。 8 4、在JAVA中如何跳出当前的多重嵌套...

    JAVA面试宝典2010

    2、编写一个程序,将d:\java目录下的所有.java文件复制到d:\jad目录下,并将原来文件的扩展名从.java改为.jad。 62 3、编写一个截取字符串的函数,输入为一个字符串和字节数,输出为按字节截取的字符串,但要保证...

Global site tag (gtag.js) - Google Analytics