1、Map接口及其实现类
Map接口同样是包含多个元素的集合,Map内存储的是“键/值对”, 这样以成对的对象组(可以把一组对象当成一个元素),通过“键”对象来查询“值”对象。Map是不同于Collection的另外一种集合接口。Map的每个元素包括两个部分:键(Key)和值(Value)。同一个Map对象中不允许使用相同的键,但是允许使用相同的值。所以Map接口隐含的有3个集合:键的集合、值的集合和映射的集合。
Map和List有一些相同之处,List中的元素是用位置确定的,元素虽然可以相同,但是位置不能相同,也就是不会出现某个位置两个元素的情况,而Map中的元素是通过键来确定的,如果把List中的位置信息看成键的话,List也可以是一种特殊的Map。
1. Map接口下常用的方法
与Collection接口相比,Map接口中主要增加了通过键进行操作的方法,就像List中增加了通过位置进行操作的方法一样。
- 添加元素的方法
public Object put(Object key,Object value)
第一个参数指定键,第二个参数指定值,如果键存在,则用新值覆盖原来的值,如果不存在添加该元素。
public void putAll(Map m),添加所有参数指定的映射。
- 获取元素的方法
public Object get(Object key)
获取指定键所对应的值,如果不存在,返回null。
- 删除元素的方法
public Object remove(Object key)
根据指定的键删除元素,如果不存在该元素,返回null。
- 与键集合、值集合和映射集合相关的操作
public Set entrySet() //获取映射的集合。
public Collection values() //获取值的集合。
public Set keySet() //返回所有键名的集合。
这3个方法的返回值不一样,因为Map中的值是允许重复的,而键是不允许重复的,当然映射也不会重复。Set不允许重复,而Collection允许重复。
- 判断是否存在指定Key和Value的方法
public boolean containsValue(Object value) //判断是否存在值为value的映射。
public boolean containsKey(Ojbect key) //判断是否存在键为key的映射。
Map接口有三个实现:
Hashtable — 主要用于存储一些映射关系。
HashMap — key/value对是按照Hash算法存储的。
TreeMap — key/value对是排序(按key排序)存储的。
2.HashTable实现类 (了解)
实现了Map接口,是同步的哈希表,不允许类型为null的键名和键值。哈希表主要用于存储一些映射关系。这个类比较特殊,与Collection中的其他类不太一样,首先它是同步的,另外它是继承自java.util.Dictionary类。
一个典型的应用就是在连接数据库的时候,需要提供各种参数,包括主机、端口、数据库ID、用户名、口令等,可以把这些信息先存储在哈希表中,然后作为参数使用。
3.HashMap实现类
是基于Hash表的Map接口实现。该类提供了所有的可选的映射操作,允许null值和null键。HashMap类和Hashtable基本相同,只是HashMap不同步,并且允许null键和null值。这个类不能保证元素的顺序,特别是顺序有可能随着时间变化。
HashMap使用了泛型,泛型的具体内容将会在2.6节讲解,读者可参看2.6节阅读本例。对于Map类型的集合,在定义对象的时候同时要指定Key的类型和Value的类型,下面的例子展示了用法:
HashMap user = new HashMap();
user.put("name","zhangsan");
user.put("sex","男");
user.put("id",135);
user.put("age",21);
HashMap对象的遍历。假设map是HashMap的对象,对map进行遍历可以使用下面两种方式:
第一种:得到元素的集合,然后进行运算,元素类型是Map.Entry。
//得到元素集合,然后转换成数组
Object[] o = map.entrySet().toArray();
// 对数组进行遍历
for(int i=0; i < map.size(); i++){
//取出数组的每一个元素
Map.Entry x = (Map.Entry)o[i];
// 获取该元素的key
Object key = x.getKey();
//获取该元素的值
Object value = x.getValue();
}
第二种:先得到所有元素的Key的集合,然后根据key得到每个key对应的value。
//先得到key的集合,然后转换成数组
Object[] o = map.keySet().toArray();
//对数组进行遍历
for(int i=0; i < o.length; i++){
//根据key得到具体的value。
Object value = map.get(o[i]);
}
HashMap使用练习。
public class HashMapDemo {
public static void main(String args[]) {
HashMap hm = new HashMap();
hm.put("tom", 20);
hm.put("john", 21);
hm.put("jack", 20);
hm.put("jones", 19);
hm.put("rose", 19);
hm.put("sun", 23);
hm.put("tom",25);
// 直接通过key值来取值
String name = "tom";
int age = (Integer) hm.get("tom");
System.out.println(name + "的年龄是" + age);
System.out.println();
// 通过Iterator迭代出key值,再通过key值取出内容
Set keys = hm.keySet();
//获得key的集合
Iterator it = keys.iterator();
//遍历key的集合,取得每个key值
while(it.hasNext()){
String key = (String)it.next();
System.out.println(key+":");
//通过每个key值,找到value
int age1 = (Integer)hm.get(key);
System.out.println(age1);
}
}
}
程序运行结果:
tom的年龄是25
rose:
19
jones:
19
jack:
20
john:
21
tom:
25
sun:
23
程序分析:HashMap中存放的value,可以直接通过key值来取值,也可通过Iterator迭代出key值,再通过key值取出内容
HashMap与HashSet的使用。
public class AccountCustomer {
public static void main(String args[]) {
Map ac = new HashMap();
Set cus1 = new HashSet();
cus1.add("SY000005");
cus1.add("SY000015");
ac.put("210103198802022273", cus1);
HashSet cus2 = new HashSet();
cus2.add("DL000123");
cus2.add("DL000321");
ac.put("210103196802022284", cus2);
HashSet cus3 = new HashSet();
cus3.add("SH000012");
ac.put("205103196802022284", cus3);
Iterator it = ac.keySet().iterator();
while (it.hasNext()) {
String customer = (String)it.next();
HashSet account = (HashSet)ac.get(customer);
System.out.print("身份号码是" + customer + "的用户的帐户:");
Iterator it2 = account.iterator();
while(it2.hasNext()){
String num = (String)it2.next();
System.out.print(num+" ");
}
/*转换成数组进行遍历
Object[] acc = account.toArray();
System.out.print("身份号码是" + customer + "的用户的帐户为");
for (int i = 0; i < acc.length; i++) {
System.out.print(acc[i] + " ");
}
*/
System.out.println();
}
}
}
程序运行结果:
身份号码是210103196802022284的用户的帐户:DL000123 DL000321
身份号码是205103196802022284的用户的帐户:SH000012
身份号码是210103198802022273的用户的帐户:SY000005 SY000015
程序分析:程序中使用HashMap来存储账号信息,每个账号的键是用来唯一表示一个客户身份的身份证号,值是HashSet类型,用来存储客户的账号,客户在银行中可以开设多个账号。程序首先使用ac.keySet().iterator()来获取所有账号信息的key,然后根据key取每个身份证对应的值,其类型为HashSet。遍历HashSet时,可以转换成数组遍历,也可以转换成迭代器进行遍历。
4. HashMap与TreeMap的比较
HashMap与TreeMap区别如下:
1.HashMap基于哈希表实现, TreeMap基于树实现。
2.HashMap可以通过调优初始容量和负载因子,优化HashMap空间的使用。
3.TreeMap没有调优选项,因为该树总处于平衡状态
4.HashMap性能优于TreeMap
HashMap与TreeMap的使用,本例使用了泛型,关于泛型的详细讲解。
import java.util.Map;
class Test {
public static void main(String args[]) throws Exception{
Map<Emp, Integer> m1= new HashMap<Emp, Integer>();
Map<Emp, Integer> m2 = new TreeMap<Emp, Integer>();
Emp emp1 = new Emp("张三");
Emp emp2 = new Emp("李四");
Emp emp3 = new Emp("王五");
Emp emp4 = new Emp("小张");
Emp emp5 = new Emp("小李");
Emp emp6 = new Emp("小王");
//Emp emp7 = new Emp("小小");
m1.put(emp1, emp1.getID());
m1.put(emp2, emp2.getID());
m1.put(emp3, emp3.getID());
m1.put(emp4, emp4.getID());
m1.put(emp5, emp5.getID());
m1.put(emp6, emp6.getID());
//m1.put(emp7, emp7.getID());
//HashMap 遍历方法
Iterator ilter1 = m1.entrySet().iterator();
while (ilter1.hasNext()) {
Map.Entry entry = (Map.Entry)ilter1.next();
Emp key = (Emp)entry.getKey();
int value = (Integer)entry.getValue();
System.out.println("Key : "+key.getName() + " Id : "+value);
}
System.out.println("*****************************");
//TreeMap遍历方法
m2.put(emp1, emp1.getID());
m2.put(emp2, emp2.getID());
m2.put(emp3, emp3.getID());
m2.put(emp4, emp4.getID());
m2.put(emp5, emp5.getID());
m2.put(emp6, emp6.getID());
Iterator ilter2= m2.entrySet().iterator();
while (ilter2.hasNext()) {
Map.Entry entry = (Map.Entry)ilter2.next();
Emp key = (Emp)entry.getKey();
int value = (Integer)entry.getValue();
System.out.println("Key : "+key.getName() + " Id : "+value);
}
}
}
class Emp implements Comparable<Emp>{
public Emp() {
this.id = 0;
}
public Emp(String name){
this.name = name;
this.id = ++emp.EmpId ;
}
public String getName(){
return this.name;
}
public int getID(){
return this.id;
}
public int compareTo(Emp o) {
return id-o.getID();
}
private static int empId = 0;
private int id;
private String name;
}
程序分析:程序中定义了一个员工类,包含员工的编号、姓名,且实现了Comparable接口,测试类中分别用HashMap与TreeMap两个类创建了存储员工信息的对象。
Map m1 = new HashMap(); //hashmap
Map m2 = new TreeMap(); //treemap
以上两条语句在创建员工对象时限制了其传入的对象类型。
5.HashMap与HashTable的比较
Hashtable(哈希表) 也可以叫做散列表,是根据关键码值(Key value)而直接进行访问的数据结构,通过把关键码值映射到表中一个位置来访问记录,以加快查找的速度。 HashTable类和HashMap用法和底层实现几乎完全一样.
1)HashMap与HashTable区别如下:
1.Hashtable是基于陈旧的Dictionary类的,HashMap是JDK1.2引进的Map接口的一个实现。
2.Hashtable是线程安全的,也就是说是同步的,而HashMap是线程序不安全的,不是同步的。
3.HashMap允许将null作为一个entry的key或者value,而Hashtable不允许。
6.如何选择集合类
实际应用中,集合类的选择主要依据如下:
1.Set内存放的元素不允许重复,List存放的元素有一定的顺序。
2.Map的应用主要在利用键/值对进行快速查询。
3.ArrayList和LinkedList的区别在于随机查询性能上ArrayList要好,但LinkedList的中间元素的插入与删除性能好。
4.Hashset和Treeset的区别在于集合内元素是否排序。
1.4 集合中的异常
在操作Java集合时,常常会遇到各种异常,其异常类型及说明如表所示。
操作集合时常见异常及说明
异常类型 | 说明 |
ClassCastException | 从集合中取得元素对象在进行类型转换的时候类型不匹配。 |
UnsupportedOperationException | 当底层实现没有实现上层的相关方法的时候由Collection抛出该异常。Collection接口(或其他集合超类)中的一些函数在java doc中是标明”可有可无(Optional)”的函数,这些函数在底层的一些具体实现中,有的实现了,有的没有实现,当我们调用底层实现集合类的这些没有实现的方法时就会抛出该异常。 |
ConcurrentModificationException | ConcurrentModificationException 当采用Iterator遍历集合时,如果此时集合中的元素被改变则Iterator遍历抛出此异常。 |
IndexOutOfBoundsException | 集合中元素引用的索引值超出界限(<0或>size())。 |
NoSuchElementException | LinkedList中getLast,getFirst等方法取元素的时候List为空。 |
集合操作中出现的java.lang.ClassCastException
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class CollectionException {
public static void main(String args[]) {
List list = new ArrayList();
list.add(new Integer(5));
list.add("aaaaa");
Iterator elements = list.iterator();
while (elements.hasNext()) {
Integer i = (Integer) elements.next();
}
}
}
程序运行结果:
Exception in thread "main" java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Integerat collection.CollectionException.main(CollectionException.java:14)
程序分析:集合里添加了个整型数据,一个字符串类型数据,在取出时把字符串转换成整型会出现类型转换异常。