在java.util中还增加了映射(Map)。映射是一个存储关键字和值的关联,或者说是“关键字/值”对的对象,即给定一个关键字,可以得到它的值。关键字和值都是对象,关键字必须是唯一的,但是可以存在相同的值。有的映射可以接收null关键字和null值,有的则不能。
可以将Map视为偶对象保存接口。Collection每一次只保存一个对象,而Map保存的是一对对象,而且这一对对象一定是按照“Key = Value”的形式保存,也就是说,可以通过Key找到对应的Value,那么这就好比使用电话本一样:
⑴ Key = 张三,Value = 110;
⑵ Key = 李四,Value = 120;
如果说现在要想找到张三的电话,那么首先应该通过张三这个Key,然后找到其对应的Value——110,如果现在保存的数据之中没有对应的Key,那么就返回null。
下表给出了支持映射的接口:
一、Map 接口
Map接口映射唯一关键字到值。关键字(key)是以后用于检索值的对象。给定一个关键字和一个值,可以存储这个值到一个Map对象中。当这个值被存储以后,就可以使用它的关键字来检索它。
Map的方法总结在下表中:
注意事项
(1)当调用的映射中没有项存在时,其中的几种方法会引发一个NoSuchElementException异常。
(2)当对象与映射中的元素不兼容时,则会引发一个ClassCastException异常。
(3)如果试图使用映射不允许使用的null对象,则会引发一个NullPointerException异常。
(4)当试图改变一个不允许修改的映射时,则会引发一个UnsupportedOperationException异常。映射不是类集,但可以获得映射的类集“视图”。为了实现这种功能,可以使用entrySet( )方法,它返回一个包含了映射中元素的集合(Set)。为了得到关键字的类集“视图”,可以使用keySet( )方法。为了得到值的类集“视图”,可以使用values()方法。类集“视图”是将映射集成到类集框架内的手段。
二、SortedMap 接口
SortedMap接口扩展了Map,它确保了各项按关键字升序排序。
由SortedMap说明的方法总结在下表中:
注意事项
(1)当调用映射中没有的项时,其中的几种方法将引发一个NoSuchElementException异常。
(2)当对象与映射中的元素不兼容时,则会引发一个ClassCastException异常。
(3)当试图使用映射不允许使用的null对象时,则会引发一个nullPointerException异常。
三、Map.Entry 接口
Map.Entry接口使得可以操作映射的输入。
如由Map接口说明的entrySet( )方法,调用该方法可返回一个包含映射输入的集合(Set),这些集合元素的每一个都是一个Map.Entry对象。
下表总结了由该接口说明的方法:
四、映射类
AbstractMap对3个具体的映射实现来说,是一个超类。AbstractMap的另一个子类——WeakHashMap实现一个使用“弱关键字”的映射,它允许映射中的元素,当该映射的关键字不再被使用时,被放入回收站。
1、HashMap 类
HashMap类使用散列表实现Map接口,它是Map接口中最为常用的子类。
HashMap允许一些基本操作,如get( )和put( )的运行时间保持恒定,即便对大型的集合也是这样的。下面的构造方法定义为:
(1)HashMap( ) :构造一个默认的散列映射。
(2)HashMap(Map m) :用m的元素初始化散列映射。
(3)HashMap(int capacity) :将散列映射的容量初始化为capacity。
(4)HashMap(int capacity, float fillRatio) :用它的参数同时初始化散列映射的容量和填充比。
填充比(也称为加载容量)
填充比必须介于0.0与1.0之间,它决定在散列集合向上调整大小之前,有多少空间被充满。具体来说,就是当元素的个数大于散列集合容量乘以它的填充比时,散列集合将被扩大。对于没有获得填充比的构造方法,默认为0.75。
HashMap实现Map并扩展AbstractMap。 它本身并没有增加任何新的方法。应该注意的是:散列映射并不保证它的元素的顺序。 因此,元素加入散列映射的顺序并不一定是它们被迭代方法读出的顺序。
package com.xy.test3;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.Map.Entry;
public class HashMapDemo1 {
public static void main(String[] args) {
// 创建HashMap对象
HashMap<String, Double> hm = new HashMap<String, Double>();
// 加入元素到HashMap中
hm.put("John Doe", new Double(3434.34));
hm.put("Tom Smith", new Double(123.22));
hm.put("Jane Baker", new Double(1378.00));
hm.put("Todd Hall", new Double(99.22));
hm.put("Ralph Smith", new Double(-19.08));
// 返回包含映射中项的集合
Set<Entry<String, Double>> set = hm.entrySet();
// 用Iterator得到HashMap中的内容
Iterator<Entry<String, Double>> itr = set.iterator();
// 显示元素
while(itr.hasNext()) {
// Map.Entry可以操作映射的输入
/* 如果用Map来取值,其过程是先从Map中取得关键字之后,
* 再返回到Map中取得相对的值
* 而用Map.Entry,可以一次性地得到所有Map中的信息
*/
Map.Entry<String, Double> me = (Map.Entry<String, Double>)itr.next();
System.out.println(me.getKey() + " : " + me.getValue());
}
System.out.println();
// 让John Doe中的值增加1000
double balance = (Double)hm.get("John Doe").doubleValue();
// 用新值替换掉旧值
hm.put("John Doe", new Double(balance + 1000));
System.out.println("John Doe现在的资金: " + hm.get("John Doe"));
}
}
【结果】
2、TreeMap 类
TreeMap类是基于红黑树(Red-Black tree)实现Map接口。TreeMap提供了按排序顺序存储关键字/值对的有效手段,同时允许快速检索。应该注意的是,不像散列映射,树映射保证它的元素按照关键字升序排序。
下面的TreeMap构造方法定义为:
(1)TreeMap( ) :构造一个空的树映射,该映射使用其关键字的自然顺序来排序。
(2)TreeMap(Comparator comp) :构造一个空的基于树的映射,该映射通过使用Comparator comp来排序。
(3)TreeMap(Map m) :用从m的输入初始化树映射,该映射使用关键字的自然顺序来排序。
(4)TreeMap(SortedMap sm) :用从sm的输入来初始化一个树映射,该映射将按与sm相同的顺序来排序。TreeMap实现SortedMap并且扩展AbstractMap,而它本身并没有另外定义其他的方法。
package com.xy.map;
import java.util.Collection;
import java.util.Iterator;
import java.util.TreeMap;
public class TreeMapDemo1 {
public static void main(String[] args) {
// 创建TreeMap对象
TreeMap<Integer, String> tm = new TreeMap<Integer, String>();
// 加入元素到TreeMap中
tm.put(10000-2000, "小红");
tm.put(10000-1500, "小四");
tm.put(10000-2500, "小明");
tm.put(10000-5000, "小光");
Collection<String> col = tm.values();
Iterator<String> itr = col.iterator();
System.out.println("工资由低到高:");
while(itr.hasNext()) {
System.out.println(itr.next());
}
}
}
【结果】
3、比较方法
TreeSet和TreeMap都按排序顺序存储元素。然而,更为“个性化”的排序顺序则需使用特定的比较方法。
通常在默认的情况下,这些类通过使用被Java称之为“自然顺序”的顺序存储它们的元素,而这种顺序通常也是你所需要的(A在B的前面,1在2的前面,等等)。如果需要用不同的方法对元素进行排序,可以在构造集合或映射时,指定一个Comparator对象(详细请参见第七部分比较器)。这样做为开发者提供了一种精确控制如何将元素储存到排序类集和映射中的能力。
下面是一个说明定制的比较方法能力的例子。该例子实现compare( )方法以便它按照正常顺序的逆向进行操作。因此,它使得一个树集合按逆向的顺序进行存储。
package com.xy.compare;
import java.util.Comparator;
import java.util.Iterator;
import java.util.TreeSet;
class MyComp implements Comparator<Object> {
public int compare(Object obj1, Object obj2) {
String aStr, bStr;
aStr = (String)obj1;
bStr = (String)obj2;
return bStr.compareTo(aStr); // 注意是bStr调用的,不是aStr
}
}
public class ComparatorDemo2 {
public static void main(String[] args) {
// 创建一个TreeSet对象
TreeSet<String> ts = new TreeSet<String>(new MyComp());
// 向TreeSet对象中加入内容
ts.add("C");
ts.add("A");
ts.add("B");
ts.add("E");
ts.add("F");
ts.add("D");
// 得到Iterator的实例化对象
Iterator<String> itr = ts.iterator();
// 显示全部内容
while(itr.hasNext()) {
Object element = itr.next();
System.out.print(element + " ");
}
}
}
【结果】
仔细观察实现Comparator并覆写compare()方法的MyComp类(正如前面所解释的那样,覆写equals( )方法既不是必需的,也不是常用的)。
在compare( )方法内部,String方法compareTo( )比较两个字符串。然而由bStr而不是aStr调用compareTo( )方法,会导致比较的结果被逆向。
对应一个更实际的例子:下面是用TreeMap程序实现存储账目资产平衡表例子的程序。下面的程序按姓对账目进行排序。为了实现这种功能,程序使用了比较方法来比较每一个账目下姓的先后顺序,得到的映射是按姓进行排序的。
package com.xy.compare;
import java.util.Comparator;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.TreeMap;
class Employee implements Comparator<Object> {
public int compare(Object obj1, Object obj2) {
int k;
String aStr, bStr;
aStr = (String)obj1;
bStr = (String)obj2;
k = aStr.compareTo(bStr);
if(k == 0) { // 相等
return aStr.compareTo(bStr);
}
else
return k;
}
}
public class ComparatorDemo3 {
public static void main(String[] args) {
// 创建TreeMap对象
TreeMap<String, Double> tm = new TreeMap<String, Double>(new Employee());
// 加入元素到TreeMap中
tm.put("John Doe", new Double(3434.34));
tm.put("Tom Smith", new Double(123.22));
tm.put("Jane Baker", new Double(1378.00));
tm.put("Todd Hall", new Double(99.22));
tm.put("Ralph Smith", new Double(-19.08));
Set<Entry<String, Double>> set = tm.entrySet();
Iterator<Entry<String, Double>> itr = set.iterator();
while(itr.hasNext()) {
Map.Entry<String, Double> me = (Map.Entry<String, Double>)itr.next();
System.out.println(me.getKey() + " : " + me.getValue());
}
System.out.println();
// 让John Doe中的值增加1000
double balance = (Double)tm.get("John Doe").doubleValue();
// 用新值替换掉旧值
tm.put("John Doe", new Double(balance + 1000));
System.out.println("John Doe现在的资金: " + tm.get("John Doe"));
}
}
【结果】
4、Map集合的输出
在Iterator接口中,我们强调过,若想输出集合的元素,可直接使用Iterator,那么在之前所有的集合都是Collection接口的子类,并且在Collection接口之中也定义了iterator()方法。但可是Map接口中却没有定义iterator()方法,所以现在如果要使用Iterator接口进行Map接口输出的话,就必须首先清楚Collection和Map接口保存对象的形式上的区别。
(1)Collection中的每一个元素都是一个独立的对象;
(2)Map中的每一个元素都是Key和Value“结伴而行”的组合对象——也就是所谓的“偶对象”。
下面可通过保存图观察形式上的区别:
Map.Entry是Map中定义的一个内部接口,而且这个接口是一个使用了static定义的外部接口,在这个接口之中定义了两个非常重要的方法。
(1)取得对应的Key的方法。public K getKey();
(2)取得对应的Value的方法。public V getValue();
那么清楚了Map.Entry的作用之后,下面就可以采用如下的步骤进行Map的Iterator输出了:
(1)通过Map接口之中entrySet()方法将Map集合变为Set集合,Set之中的泛型类型为Map. Entry;
(2)利用Set接口之中的iterator()方法取得Iterator接口对象,此时的泛型类型依然为Map.Entry;
(3)利用Iterator迭代出每一个Map.Entry对象,再使用getKey()和getValue()方法取出内容。
package com.xy.map;
import java.util.Map;
import java.util.Set;
import java.util.HashMap;
import java.util.Iterator;
public class IteratorMapDemo1 {
public static void main(String[] args) {
Map<Integer, String> map = new HashMap<Integer, String>();
map.put(1, "小三");
map.put(2, "小四");
map.put(3, "小五");
Set<Map.Entry<Integer, String>> set = map.entrySet();
Iterator<Map.Entry<Integer, String>> itr = set.iterator();
while(itr.hasNext()) {
Map.Entry<Integer, String> me = itr.next();
System.out.println(me.getKey() + " : " + me.getValue());
}
}
}
【结果】