【JAVA集合框架之Map】
一、概述。
1.Map是一种接口,在JAVA集合框架中是以一种非常重要的集合。
2.Map一次添加一对元素,所以又称为“双列集合”(Collection一次添加一个元素,所以又称为“单列集合”)
3.Map集合中存放的是一个一个的键值对,集合中存放的元素必须保证键的唯一性。
二、常用方法。
1.添加
V |
put(K key, V value) 将指定的值与此映射中的指定键关联(可选操作)。 |
该方法的作用就是向集合中添加一个键值对,并返回一个值;如果键存在,则返回对应的旧值,并以新值取代之;如果键不存在则返回null。所以该方法也是修改的方法。
void |
putAll(Map<? extends K,? extends V> m) 从指定映射中将所有映射关系复制到此映射中(可选操作)。 |
该方法功能略,但注意泛型上限的使用。比较Collection方法:
boolean |
addAll(Collection<? extends E> c) 将指定 collection 中的所有元素都添加到此 collection 中(可选操作)。 |
2.删除
V |
remove(Object key) 如果存在一个键的映射关系,则将其从此映射中移除(可选操作)。 |
该方法根据键删除一个键值对,并返回值。如果没有这个键值对,将返回null。
void |
clear() 从此映射中移除所有映射关系(可选操作)。 |
该方法功能就是清空集合。
3.判断
boolean |
containsKey(Object key) 如果此映射包含指定键的映射关系,则返回 true。 |
boolean |
containsValue(Object value)
如果此映射将一个或多个键映射到指定值,则返回 true。 |
这两个方法,前者判断是否存在指定键,后者判断是否存在指定值。
boolean |
isEmpty() 如果此映射未包含键-值映射关系,则返回 true。 |
该方法用于判断该Map集合是否为空集合。
4.获取。
V |
get(Object key) 返回指定键所映射的值;如果此映射不包含该键的映射关系,则返回 null 。 |
int |
size() 返回此映射中的键-值映射关系数。 |
这两个方法,前者根据指定的键获取对应的值,如果集合中没有指定的键,则返回null;后者获取键值对的个数。
以上的方法为基本方法,重点方法在下面。
三、重点:Map集合的四种遍历方式。
1.第一种遍历方式:使用keySet方法。
Set<K> |
keySet() 返回此映射中包含的键的 Set 视图。 |
该方法会返回一个包含所有键的Set集合。
遍历过程:先得到所有键的集合,遍历集合,根据键得到所有的值。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 package p01.traverseDemo01.keySetDemo; 2 3 import java.util.HashMap; 4 import java.util.Iterator; 5 import java.util.Map; 6 import java.util.Set; 7 8 public class KeySetDemo { 9 10 public static void main(String[] args) { 11 Demo01(); 12 } 13 14 private static void Demo01() { 15 Map<Integer,String>map=new HashMap<Integer,String>(); 16 map.put(1, "wangcai"); 17 map.put(2, "xiaoqiang"); 18 map.put(3, "xiaoming"); 19 20 Set<Integer>set=map.keySet(); 21 for(Iterator<Integer>it=set.iterator();it.hasNext();) 22 { 23 Integer key=it.next(); 24 String value=map.get(key); 25 System.out.println(key+":"+value); 26 } 27 28 } 29 30 }
应当注意,结果是无序的,这是因为采用了HashMap作为示例,HashMap底层的数据结构是哈希表,存储元素的时候有自己的规则,所以无序。
2.第二种遍历方式:使用entrySet方法。
Set<Map.Entry<K,V>> |
entrySet() 返回此映射中包含的映射关系的 Set 视图。 |
此方法返回值也是一个Set集合,但是存储的内容是Map.Entry对象。Map.Entry是什么东西?
JDK1.6API的解释如下:
嵌套类摘要 | |
---|---|
static interface |
Map.Entry<K,V> 映射项(键-值对)。 |
虽然API的解释是“嵌套类”,但是Map是一个接口,Entry也是一个接口,事实上Map.Entry是Map的一个内部静态接口。
该接口封装了几个方法,用于操作一个键值对,注意,是一个,因为它仅仅对一个键值对进行封装(确切的说是对操作每个键值对的方法进行了封装,每个键值对对应着一个Map.Entry对象)。
Map.Entry接口的方法如下:
方法摘要 | |
---|---|
boolean |
equals(Object o) 比较指定对象与此项的相等性。 |
K |
getKey()
返回与此项对应的键。 |
V |
getValue()
返回与此项对应的值。 |
int |
hashCode()
返回此映射项的哈希码值。 |
V |
setValue(V value)
用指定的值替换与此项对应的值(可选操作)。 |
以上方法中,最常用的是getKey方法和getValue方法。
遍历过程:首先拿到每一个键值对对象Map.Entry的集合,再通过遍历集合拿到所有的键值对。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 package p01.traverseDemo01.keySetDemo; 2 3 import java.util.HashMap; 4 import java.util.Iterator; 5 import java.util.Map; 6 import java.util.Set; 7 8 public class KeySetDemo { 9 10 public static void main(String[] args) { 11 Demo01(); 12 } 13 14 private static void Demo01() { 15 Map<Integer,String>map=new HashMap<Integer,String>(); 16 map.put(1, "wangcai"); 17 map.put(3, "xiaoming"); 18 map.put(2, "xiaoqiang"); 19 20 21 Set<Integer>set=map.keySet(); 22 for(Iterator<Integer>it=set.iterator();it.hasNext();) 23 { 24 Integer key=it.next(); 25 String value=map.get(key); 26 System.out.println(key+":"+value); 27 } 28 29 } 30 31 }
3.第三种遍历方式:使用values方法。
Collection<V> |
values() 返回此映射中包含的值的 Collection 视图。 |
此种方法可以返回值的Collection集合,比较返回值,第一种方式使用keySet方法,返回值类型是Set集合,这里为什么不是Set集合?
解析:Map中键是唯一的,所以使用Set集合存储,而值可以重复,所以使用Collection集合存储。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 package p01.traverseDemo03.valuesDemo; 2 3 import java.util.Collection; 4 import java.util.HashMap; 5 import java.util.Iterator; 6 import java.util.Map; 7 import java.util.Set; 8 9 public class ValuesDemo { 10 11 public static void main(String[] args) { 12 Demo01(); 13 } 14 15 private static void Demo01() { 16 Map<Integer,String>map=new HashMap<Integer,String>(); 17 map.put(1, "wangcai"); 18 map.put(3, "xiaoming"); 19 map.put(2, "xiaoqiang"); 20 21 22 Collection<String>coll=map.values(); 23 for(Iterator<String>it=coll.iterator();it.hasNext();) 24 { 25 String value=it.next(); 26 System.out.println(value); 27 } 28 29 } 30 31 }
4.第四种遍历方式:使用增强型for循环间接遍历。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 package p11.ArraysDemo.p01.ExtendForCircle; 2 3 import java.util.Collection; 4 import java.util.HashMap; 5 import java.util.Map; 6 import java.util.Set; 7 8 /** 9 * 使用增强型for循环实现Map的三种遍历方式 10 * @author kuangdaoyizhimei 11 * 12 */ 13 public class ExtendForCircle { 14 15 public static void main(String[] args) { 16 Map<Integer,String>map=new HashMap<Integer,String>(); 17 map.put(1, "xiaoqiang"); 18 map.put(2, "zhangsan"); 19 map.put(3, "lisi"); 20 map.put(4, "wangwu"); 21 Function1(map); 22 Function2(map); 23 Function3(map); 24 } 25 26 private static void Function3(Map<Integer, String> map) { 27 Set<Map.Entry<Integer, String>>set=map.entrySet(); 28 for(Map.Entry<Integer, String>kv:set) 29 { 30 Integer key=kv.getKey(); 31 String value=kv.getValue(); 32 System.out.println("Key:"+key+"\tvalue:"+value); 33 } 34 } 35 36 private static void Function2(Map<Integer, String> map) { 37 Collection<String>list=map.values(); 38 for(String val:list) 39 { 40 System.out.println(val); 41 } 42 System.out.println(); 43 System.out.println(); 44 } 45 46 private static void Function1(Map<Integer, String> map) { 47 Set<Integer>set=map.keySet(); 48 for(Integer i:set) 49 { 50 System.out.println(i+":"+map.get(i)); 51 } 52 System.out.println(); 53 System.out.println(); 54 } 55 56 }
四、Map集合的常见子类。
public class Hashtable<K,V>extends Dictionary<K,V>implements Map<K,V>, Cloneable, Serializable |
public class HashMap<K,V>extends AbstractMap<K,V>implements Map<K,V>, Cloneable, Serializable |
public class TreeMap<K,V>extends AbstractMap<K,V>implements NavigableMap<K,V>, Cloneable, Serializable |
这三者有些区别:
HashTable:内部结构是哈希表,JDK1.0就出现了(JDK1.0出现的单列集合只有Vector,双列集合只有HashTable),是同步的。不允许null作为键值。
HashMap:内部结构是哈希表,是非同步的。允许null作为键值。
TreeMap:内部结构是一棵红黑树,可以对Map集合中的键进行排序。
需要注意的是Set集合的实现依赖于HashMap集合----HashMap将值统一为一个对象而只关注键的操作即可实现Set集合中元素的唯一性。
HashTable有一个子类应当特别注意:Properties
public class Propertiesextends Hashtable<Object,Object> |
这个类用来存储键值对型的文件配置信息,它和Io技术相结合才能发挥出它的优势。
1.HashMap类。
Hash代表着底层的实现使用了哈希表,所以如果使用自定义对象作为键,应当注意重写类的hashCode方法和equals方法。
示例:使用HashMap存储自定义对象Student和其归属地NativePlace,并遍历。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 package p02.SubClassDemo01.HashMapDemo; 2 3 import java.util.HashMap; 4 import java.util.Iterator; 5 import java.util.Map; 6 7 class Student 8 { 9 private String name; 10 private int age; 11 @Override 12 public int hashCode() { 13 final int prime = 31; 14 int result = 1; 15 result = prime * result + age; 16 result = prime * result + ((name == null) ? 0 : name.hashCode()); 17 return result; 18 } 19 @Override 20 public boolean equals(Object obj) { 21 if (this == obj) 22 return true; 23 if (obj == null) 24 return false; 25 if (getClass() != obj.getClass()) 26 return false; 27 Student other = (Student) obj; 28 if (age != other.age) 29 return false; 30 if (name == null) { 31 if (other.name != null) 32 return false; 33 } else if (!name.equals(other.name)) 34 return false; 35 return true; 36 } 37 public Student(String name, int age) { 38 super(); 39 this.name = name; 40 this.age = age; 41 } 42 public Student() { 43 super(); 44 } 45 public String getName() { 46 return name; 47 } 48 public void setName(String name) { 49 this.name = name; 50 } 51 public int getAge() { 52 return age; 53 } 54 public void setAge(int age) { 55 this.age = age; 56 } 57 } 58 59 class NativePlace 60 { 61 private String province; 62 private String city; 63 public NativePlace(String province, String city) { 64 super(); 65 this.province = province; 66 this.city = city; 67 } 68 public NativePlace() { 69 super(); 70 } 71 public String getProvince() { 72 return province; 73 } 74 public void setProvince(String province) { 75 this.province = province; 76 } 77 public String getCity() { 78 return city; 79 } 80 public void setCity(String city) { 81 this.city = city; 82 } 83 } 84 public class HashMapDemo { 85 86 public static void main(String[] args) { 87 Demo01(); 88 } 89 90 private static void Demo01() { 91 HashMap<Student,NativePlace>hm=new HashMap<Student,NativePlace>(); 92 hm.put(new Student("wangwu",25), new NativePlace("hubei","suizhou")); 93 hm.put(new Student("zhaoliu",26), new NativePlace("hunan","chagnsha")); 94 hm.put(new Student("zhangsan",23), new NativePlace("shandong","zibo")); 95 hm.put(new Student("lisi",24), new NativePlace("shandong","weifang")); 96 97 //重复添加无效 98 hm.put(new Student("wangwu",25), new NativePlace("hubei","suizhou")); 99 hm.put(new Student("zhaoliu",26), new NativePlace("hunan","chagnsha")); 100 hm.put(new Student("zhangsan",23), new NativePlace("shandong","zibo")); 101 hm.put(new Student("lisi",24), new NativePlace("shandong","weifang")); 102 103 for(Iterator<Map.Entry<Student,NativePlace>>it=hm.entrySet().iterator();it.hasNext();) 104 { 105 Map.Entry<Student, NativePlace>me=it.next(); 106 Student stu=me.getKey(); 107 NativePlace np=me.getValue(); 108 System.out.println(stu.getName()+":"+np.getProvince()+"."+np.getCity()); 109 } 110 111 112 } 113 114 }
2.TreeMap类。
TreeMap类底层使用了排序树,如果使用自定义对象作为键,就必须实现Comparable接口;如果没有实现这个接口,则必须使用比较器作为HashTree初始化对象的参数。
现在将上面的代码中Student的hashCode方法以及equals方法删掉,同时实现接口Comparable,重写compareTo方法,以达到按照年龄排序的目的。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 package p02.SubClassDemo02.TreeMapDemo; 2 3 import java.util.Iterator; 4 import java.util.Map; 5 import java.util.TreeMap; 6 7 class Student implements Comparable<Student> 8 { 9 private String name; 10 private int age; 11 public Student(String name, int age) { 12 super(); 13 this.name = name; 14 this.age = age; 15 } 16 public Student() { 17 super(); 18 } 19 public String getName() { 20 return name; 21 } 22 public void setName(String name) { 23 this.name = name; 24 } 25 public int getAge() { 26 return age; 27 } 28 public void setAge(int age) { 29 this.age = age; 30 } 31 @Override 32 public int compareTo(Student o) { 33 int temp=this.age-o.getAge(); 34 return temp==0?this.name.compareTo(o.getName()):temp; 35 } 36 } 37 38 class NativePlace 39 { 40 private String province; 41 private String city; 42 public NativePlace(String province, String city) { 43 super(); 44 this.province = province; 45 this.city = city; 46 } 47 public NativePlace() { 48 super(); 49 } 50 public String getProvince() { 51 return province; 52 } 53 public void setProvince(String province) { 54 this.province = province; 55 } 56 public String getCity() { 57 return city; 58 } 59 public void setCity(String city) { 60 this.city = city; 61 } 62 } 63 64 public class TreeMapDemo { 65 66 public static void main(String[] args) { 67 Demo01(); 68 } 69 70 private static void Demo01() { 71 TreeMap<Student,NativePlace>hm=new TreeMap<Student,NativePlace>(); 72 hm.put(new Student("wangwu",25), new NativePlace("hubei","suizhou")); 73 hm.put(new Student("zhaoliu",26), new NativePlace("hunan","chagnsha")); 74 hm.put(new Student("zhangsan",23), new NativePlace("shandong","zibo")); 75 hm.put(new Student("lisi",24), new NativePlace("shandong","weifang")); 76 77 //重复添加无效 78 hm.put(new Student("wangwu",25), new NativePlace("hubei","suizhou")); 79 hm.put(new Student("zhaoliu",26), new NativePlace("hunan","chagnsha")); 80 hm.put(new Student("zhangsan",23), new NativePlace("shandong","zibo")); 81 hm.put(new Student("lisi",24), new NativePlace("shandong","weifang")); 82 83 for(Iterator<Map.Entry<Student,NativePlace>>it=hm.entrySet().iterator();it.hasNext();) 84 { 85 Map.Entry<Student, NativePlace>me=it.next(); 86 Student stu=me.getKey(); 87 NativePlace np=me.getValue(); 88 System.out.println(stu.getName()+":"+stu.getAge()+"----"+np.getProvince()+"."+np.getCity()); 89 } 90 91 92 } 93 94 }
运行可以看出重复添加的元素仍然没有添加进去,因为TreeSet判断元素是否相同的依据是compareTo方法的返回值而不是hashCode方法以及equals方法。
如果现在有了新的需求,需要按照新的规则对学生进行排序,则创建比较器,按照比较器中的新规则进行排序。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 package p02.SubClassDemo02.TreeMapDemo; 2 3 import java.util.Comparator; 4 import java.util.Iterator; 5 import java.util.Map; 6 import java.util.TreeMap; 7 8 class Student implements Comparable<Student> 9 { 10 private String name; 11 private int age; 12 public Student(String name, int age) { 13 super(); 14 this.name = name; 15 this.age = age; 16 } 17 public Student() { 18 super(); 19 } 20 public String getName() { 21 return name; 22 } 23 public void setName(String name) { 24 this.name = name; 25 } 26 public int getAge() { 27 return age; 28 } 29 public void setAge(int age) { 30 this.age = age; 31 } 32 @Override 33 public int compareTo(Student o) { 34 int temp=this.age-o.getAge(); 35 return temp==0?this.name.compareTo(o.getName()):temp; 36 } 37 } 38 39 class NativePlace 40 { 41 private String province; 42 private String city; 43 public NativePlace(String province, String city) { 44 super(); 45 this.province = province; 46 this.city = city; 47 } 48 public NativePlace() { 49 super(); 50 } 51 public String getProvince() { 52 return province; 53 } 54 public void setProvince(String province) { 55 this.province = province; 56 } 57 public String getCity() { 58 return city; 59 } 60 public void setCity(String city) { 61 this.city = city; 62 } 63 } 64 65 class CompareByName implements Comparator<Student> 66 { 67 @Override 68 public int compare(Student o1, Student o2) { 69 int temp=o1.getName().compareTo(o2.getName()); 70 return temp==0?o1.getAge()-o2.getAge():temp; 71 } 72 } 73 public class TreeMapDemo { 74 public static void main(String[] args) { 75 Demo01(); 76 } 77 78 private static void Demo01() { 79 TreeMap<Student,NativePlace>hm=new TreeMap<Student,NativePlace>(new CompareByName()); 80 hm.put(new Student("wangwu",25), new NativePlace("hubei","suizhou")); 81 hm.put(new Student("zhaoliu",26), new NativePlace("hunan","chagnsha")); 82 hm.put(new Student("zhangsan",23), new NativePlace("shandong","zibo")); 83 hm.put(new Student("lisi",24), new NativePlace("shandong","weifang")); 84 85 //重复添加无效 86 hm.put(new Student("wangwu",25), new NativePlace("hubei","suizhou")); 87 hm.put(new Student("zhaoliu",26), new NativePlace("hunan","chagnsha")); 88 hm.put(new Student("zhangsan",23), new NativePlace("shandong","zibo")); 89 hm.put(new Student("lisi",24), new NativePlace("shandong","weifang")); 90 91 for(Iterator<Map.Entry<Student,NativePlace>>it=hm.entrySet().iterator();it.hasNext();) 92 { 93 Map.Entry<Student, NativePlace>me=it.next(); 94 Student stu=me.getKey(); 95 NativePlace np=me.getValue(); 96 System.out.println(stu.getName()+":"+stu.getAge()+"----"+np.getProvince()+"."+np.getCity()); 97 } 98 99 100 } 101 102 }
可以看到,通过使用比较器,确实达到了按照名字字典序排序的目的,这也证明了如果使用比较器,则原来的自然比较方式将会无效。
3.LinkedHashMap类。
此类的是HashMap的子类,与HashMap相比,该类实现了“有序”,即插入和取出的顺序一致。该类在很多时候都能发挥出很大的作用。