1.Map集合概述:
/* Map<K,V>: K (key)- 此映射所维护的键的类型 V (Value)- 映射值的类型 Map集合:该集合存储键值对.一对一对往里存.而且要保证键的唯一性 */ package map; import java.util.*; class MapDemo { public static void main(String[] args) { //测试LinkedHashMap->存入顺序和取出顺序一致 //此链接列表定义了迭代顺序,该迭代顺序通常就是将键插入到映射中的顺序(插入顺序) LinkedHashMap<String,String>lm = new LinkedHashMap<String,String>(); lm.put("1","abc"); lm.put("2","bcd"); lm.put("3","abc"); lm.put("4","abcd"); lm.put("5","abc"); System.out.println(lm); lm.put("5","defg"); System.out.println(lm); } } /* 重要的实现类: Hashtables:底层是哈希表数据结构,不可以存入null键null值.HashTable是线程同步的.效率低 JDK 1.0 为了成功地在哈希表中存储和获取对象,用作键的对象必须实现 hashCode 方法和 equals 方法。 HashMap:底层是哈希表数据结构,可以存入null键null值.HashMap是线程非同步的.效率高 JDK1.2 依然需要实现hashCode和equals TreeMap:①底层是红黑树(自平衡二叉查找树)数据结构.线程不同步. ②可以用于给map集合中的键(Key)进行排序和Set很像. 其实,set底层就是使用了Map集合 HashSet 底层用到了 HashMap 很好解释了为什么要复写hashCode和equals TreeSet 底层用到了 TreeMap 而TreeMap运用的就是红黑树 */
2.Map常用操作:
/* 一些常用操作: 1.添加 V put(K key, V value) 将指定的值与此映射中的指定键关联(可选操作)。 void putAll(Map<? extends K,? extends V> m) 从指定映射中 将所有映射关系 复制到此映射中(可选操作)。 2.删除 void clear() V remove(Object key) 如果存在一个键的映射关系,则将其从此映射中移除(可选操作)。 3.判断 boolean containsKey(Object key) 如果此映射包含 指定键的映射关系,则返回true. boolean containsValue(Object Value) 如果此映射将 一个或多个键映射到指定值 ,则返回true. 4.获取 V get(Object key) 返回指定键所映射的值;如果此映射不包含该键的映射关系,则返回 null。 int size() 返回此映射中的 键-值映射 关系数。 Collection<V> values() 返回此映射中包含的值(Value)的 Collection 视图。 ※Set<Map.Entry<K,V>> entrySet() 返回此映射中包含的映射关系的 Set 视图。 ※ Set<K> keySet() 返回此映射中包含的键的 Set 视图。 */ package map; import java.util.*; class MapDemo2{ public static void print(Object obj){ System.out.println(obj); } public static void main(String[] args){ Map<String,String> map = new HashMap<String,String>(); //添加元素 print("put "+map.put("01","zhangsan"));//null 原来HashMap中没有01对应的value->返回null print("put "+map.put("01","heihei"));//zhangsan 返回旧的01对应的heihei,并且把01对应的zhangsan替换掉(保证key唯一性) /* 也就是说,在添加时,新的key=value与旧的key=value 出现key相同->新value会覆盖旧value,并且返回旧value. */ map.put("02","Lisi"); map.put("03","wangwu"); print("containsKey: "+map.containsKey("04"));//false print("remove04: "+map.remove("04"));//null print("remove02: "+map.remove("02"));//Lisi print(map);//{01=zhangsan,03=wangwu}//依然元素转换成字符串形式输出,使用的是AbstractMap中的toString(); //注意打印顺序和存入顺序可能不同->底层使用的为哈希表 //获取 print("get01: "+map.get("01"));//zhangsan print("get04: "+map.get("04"));//null map.put(null,"maliu");//虽然可以加入,但没有意义 map.put("04",null);//同上 //可以通过get方法的返回值来判断一个键是否存在,返回是否为null判断 Collection<String> coll = map.values(); print(coll); print(map); } }
3.entrySet()与keySet()
/* 怎样将Map中所有value,Key取出? 1.Set<K> keySet() 将Map中所有的Key存入到Set集合. 因为set集合具备迭代器.利用迭代器取出Key, 根据Map中的get方法, 获取每一个键对应的值. Map集合的取出原理:将map集合转成set集合,在通过迭代器取出 (画一个示意图) 2.Set<Map.Entry<K,V>> entrySet() 将map结合中的映射关系存入到了set集合中,而这个关系的数据类型就是Map.Entry (画一个示意图) Map.Entry:表示Map中的关系类型 而Map.Entry<K,V>:指定关系中Key和Value的操作类型 Set<Map.Entry<K,V>>:泛型嵌套 老毕经典比喻:(霸气侧漏(*^__^*)) 假如存入的键值对为丈夫和妻子(当然了一夫一妻制:一个丈夫对应一个妻子) ①keySet:把Map中的丈夫取出来放在Set->通过丈夫找出对应的妻子 ②entrySet:把Map中的丈夫和妻子的关系取出来(假设为结婚证)->存放在Set中 ->通过结婚证,结婚证上有丈夫和妻子啊(Map.Entry) ->调用结婚证特有方法(getKey,getValue) ->获取到丈夫和妻子 */ package map; import java.util.*; class MapDemo3{ public static void main(String[] args){ Map<String,String> map = new HashMap<String,String>(); map.put("02","zhang"); map.put("03","wang"); map.put("04","xu"); //获取map集合中所有key的Set集合 Set<String> s = map.keySet();//Set<String>里面指明key的泛型,因为获取的是key的集合 for(Iterator<String>it = s.iterator();it.hasNext();) System.out.println(map.get(it.next())); //entrySet方法,注意用的HashMap存入和取出的顺序不一定一致 Set<Map.Entry<String,String>> s2= map.entrySet(); for(Iterator<Map.Entry<String,String>> it2 = s2.iterator();it2.hasNext(); ){ Map.Entry<String,String> me=it2.next(); System.out.println(me.getKey()+"..."+me.getValue()); } } } /* 分析Map.Entry: 其实Entry也是一个接口,它是Map接口中的一个内部接口. src中: //接口经过编译后生成.class文件,可以类比下 内部类 public interface Map<K,V>{ public static interface Entry<K,V>{ K getKey(); V getValue(); V setValue(V value); } } 为什要把Entry定义在Map内部? Entry表示Map中Key与Value的关系,其实Key和Value被封装在了Entry类(HashMap中一个内部类实现了Map中的Entry接口)中.
static class Entry<K,V> implements Map.Entry<K,V>{ final K key; V value; Entry<K,V> next; int hash; Entry(int h, K k, V v, Entry<K,V> n) { value = v; next = n; key = k; hash = h; } public final K getKey() { return key; } public final V getValue() { return value; } public final V setValue(V newValue) { V oldValue = value; value = newValue; return oldValue; } ..... } */
keySet()方法:
entrySet()方法:
4.HashMap集合示例:
/* 每一个学生都有对应的归属地 学生Student,地址String. 学生属性:姓名和年龄相同的视为同一个学生 保证学生的唯一性 */ //描述学生 //注意这里需要存入到HashMap中 //之前HashSet之所以需要复写hashCode和equals //因为底层用的其实是HashMap(更确切的说是哈希表) //为了能够实现对哈希表中的元素进行存储和获取->用作Key的对象需要复写这两个方法 //要想存入到HashMap中,更需要复写 package map; import java.util.*; class Student implements Comparable<Student>{//如果Studet继承Person使用Person扩展性更好 private String name; private int age; Student(String name,int age){ this.name=name; this.age=age; } public int hashCode(){ return name.hashCode()+age*37; } public boolean equals(Object obj){ if(!(obj instanceof Student)) // return false; 返回false没意义,因为不同对象比较没意义 throw new ClassCastException("类型不匹配");//比较好 else return this.name==((Student)obj).name && this.age==((Student)obj).age; } //按姓名比较 public int compareTo(Student stu){ int num=this.name.compareTo(stu.name); if(num==0) return this.age-stu.age; else return num; } public String getName(){ return name; } public int getAge(){ return age; } } class MapTest{ public static void main(String[] args){ Map<Student,String> map = new HashMap<Student,String>(); map.put(new Student("01",12),"abc"); map.put(new Student("01",12),"bcd");//与第一个key相同,value不同 map.put(new Student("01",12),"abc");//与第一个key,value均相同 map.put(new Student("02",23),"abc");//与第一个key不同,value相同 /* 取出结果: entrySet: keySet: 01-12-abc abc 02-23-abc abc */ //keySet //Set<Student> s = map.keySet(); for(Iterator<Student> it = map.keySet().iterator();it.hasNext(); ){ System.out.println(map.get(it.next())); } //entrySet //Set<Map.Entry<Student,String>> s2 = map.entrySet(); for(Iterator<Map.Entry<Student,String>> it = map.entrySet().iterator();it.hasNext(); ){ Map.Entry<Student,String> me=it.next(); Student stu = me.getKey(); System.out.println(stu.getName()+"..."+stu.getAge()+"..."+me.getValue()); } } }
5.TreeMap示例:
/* TreeMap: 底层数据结构:红黑树(Red-Black tree) 该映射根据其键的自然顺序进行排序, 或者根据创建映射时提供的 Comparator 进行排序, 具体取决于使用的构造方法。 TreeMap中不允许存入null Key,会发出NullPointerException,但可以存入null value. 因为null Key与null Key比较无意义. */ import java.util.*; class TreeMapDemo{ public static void main(String[] args){ TreeMap<String,String>tm = new TreeMap<String,String>(); //tm.put(null,"123");//NullPointerException tm.put("123","12"); //tm.containsKey(null);//NullPointerException // tm.get(null);//NullPointerException } }
/* 需求:对学生对象的年龄进行升序排序 因为数据是以键值对形式存在的. 所以要使用可以排序的Map集合-TreeMap */ package map; import java.util.*; //自定义比较器,按年龄进行比较 class MyComp implements Comparator<Student>{ public int compare(Student stu_1,Student stu_2){ int num=stu_1.getAge()-stu_2.getAge(); if(num==0) return stu_1.getName().compareTo(stu_2.getName()); return num; } } class MapTest2{ public static void printMap(Map<Student,String> map){ for(Iterator<Map.Entry<Student,String>> it = map.entrySet().iterator();it.hasNext(); ){ // map.entrySet().iterator();//首先获得一个存储关系对象的集合实体 Map.Entry<Student,String> me=it.next(); //调用iterator()获取一个相应的迭代器实体 Student stu = me.getKey(); System.out.println(stu.getName()+"..."+stu.getAge()+"..."+me.getValue()); } } public static void main(String[] args){ Map<Student,String> map = new TreeMap<Student,String>(new MyComp()); map.put(new Student("zhang",18),"BJ"); map.put(new Student("liu",12),"SH"); map.put(new Student("wang",20),"NJ"); map.put(new Student("xu",16),"TJ"); //在构造一个新的TreeMap,使用自然顺序排序 Map<Student,String> map2 = new TreeMap<Student,String>(map); System.out.println(map);//输出map集合,其中按Key从小到大顺序排列 //但这样输出的Key为对象字符串形式. printMap(map); System.out.println("\n"); printMap(map2); } }
6.TreeMap练习2:
/* "sdfgzxcvasdfxcvdf" 获取该字符串中的字母出现次数 希望打印结果:a(1)c(2)...... 每一个字母都有对应的次数. 说明字母和次数之间都有映射关系 当发现有映射关系(一个对应一个)时,可以选择map集合 什么时候使用map集合? 像上面的情况那样(数据之间存在映射关系)->首先考虑map集合 思想: ①首先把字符串转化成字符数组 ②拿着字符去map集合中遍历->遍历完未找到a(可以令其返回null)->存入a,1(表示出现一次) ->找到转③ ③说明a在map中存在对应的key,此时,取出a对应的value->令value自增->重新存入 ④其余字符同理 */ package map; import java.util.*; class TreeMapTest{ //方法一:利用containsKey public static String charCount(String str,Map<Character,Integer> tm){ char[] ch = str.toCharArray();//转换成字符数组,便于操作 for(int i=0;i<ch.length;++i){ Character c=new Character(ch[i]); if(!tm.containsKey(c))//没有该Key,加入Map集合 tm.put(c,new Integer(1)); else//有,则替换掉该key对应的value tm.put(c,new Integer(tm.get(c).intValue()+1)); } StringBuilder sb = new StringBuilder(); //利用entrySet->转换成指定格式的字符串返回 for(Iterator<Map.Entry<Character,Integer>>it=tm.entrySet().iterator();it.hasNext();){ Map.Entry<Character,Integer> me=it.next(); sb.append("("+me.getKey()+")"+me.getValue()); } return sb.toString(); } //方法二:利用get public static String charCount_2(String str,Map<Character,Integer> tm){ char[] ch = str.toCharArray(); for(int i=0,count=0;i<ch.length;++i,count=0){ if((ch[i]<'a'||ch[i]>'z')&&(ch[i]<'A'||ch[i]>'Z'))//非字母字符,不再计数 continue; Integer value=tm.get(ch[i]); if(value!=null) count=value;//count简化了两次调用put ++count; tm.put(ch[i],count); /* if(value==null)//Key没有对应的Value,返回null,加入集合 tm.put(ch[i],1); else tm.put(ch[i],value+1); */ } String mapStr="";//存储集合字符串形式 //利用entrySet->转换成指定格式的字符串返回 for(Iterator<Map.Entry<Character,Integer>>it=tm.entrySet().iterator();it.hasNext();){ Map.Entry<Character,Integer> me=it.next(); mapStr += "("+me.getKey()+")"+me.getValue(); } return mapStr; } public static void main(String[] args){ TreeMap<Character,Integer> tm = new TreeMap<Character,Integer>(); String str = "abae+c"; //System.out.println(charCount(str,tm)); //如果再次使用同一个Map,第二种方法,在遍历a时,会把a对应的Value+1 System.out.println(charCount_2(str,tm)); } } /* //TreeMap/HashMap在使用迭代器方法时,使用了集合的增,删方法时,也会报并发修改异常 tm.put('b',1); while(it.hasNext()) { tm.put('a',1); System.out.println(it.next()); } */ /* 总结:该题和以前做过的一个去除集合中的重复元素 使用的同一个算法: ①新建一个集合容器 ②每次添加看看该容器是否包含添加的元素 ③有则不在添加,没有则加入. */
7.Map扩展(Map中嵌套Map/Set/List)
①Map集合的Value为Map
/*
<掌握这个思想> Map扩展:(一对多映射/集合中嵌套集合/集合中存取集合对象) 例如: 预热班-01-zhangsan 预热班-02-lisi 就业班-01-maliu (示意图) */ package map; import java.util.*; class MapExtend{ //取出学生:先找学校->再找班级->最后取学生 public static void getStu(Map<String,HashMap<String,String>>map,String classes){ //方法一:利用entrySet只取预热班学生 for(Iterator<Map.Entry<String,HashMap<String,String>>> it = map.entrySet().iterator();it.hasNext(); ){ Map.Entry<String,HashMap<String,String>> mn=it.next(); if(mn.getKey()=="预热班"){//如果把该条件去掉,取出所有班级的学生 for(Iterator<Map.Entry<String,String>>itValue=mn.getValue().entrySet().iterator();itValue.hasNext();){ Map.Entry<String,String> me = itValue.next(); System.out.println(mn.getKey()+"..."+me.getKey()+"..."+me.getValue()); } } } } //方法二 熟练下keySet public static void getStu_2(Map<String,HashMap<String,String>>map,String classes){ //利用entrySet只取预热班学生 for(Iterator<String> it = map.keySet().iterator();it.hasNext(); ){//遍历取出班级 String bj= it.next();//存放班级 if(bj=="就业班"){ HashMap<String,String> hm;//为了存放下面取出的集合对象 for(Iterator<String> itValueKey = (hm=map.get(bj)).keySet().iterator();itValueKey.hasNext(); ){ String number = itValueKey.next(); System.out.println(classes+"..."+number+"..."+hm.get(number)); } } } } public static void main(String[] args){ //类比:先有学校->再有教室->最后有了学生 TreeMap<String,HashMap<String,String>> tm = new TreeMap<String,HashMap<String,String>>();//集合中存入集合对象 HashMap<String,String>hm_1 = new HashMap<String,String>();//第一个教室 HashMap<String,String> hm_2 = new HashMap<String,String>();//第二个教室 tm.put("预热班",hm_1);//添加班级 tm.put("就业班",hm_2); hm_1.put("01","zhangsan");//添加学生 hm_1.put("02","lisi"); hm_2.put("03","zhangsan"); hm_2.put("04","lisi"); System.out.println(tm); getStu(tm,"预热班"); System.out.println(); getStu_2(tm,"就业班"); } }
②Map中的Value为List集合
/* 对于前面那个例子: 预热班-01-zhangsan 预热班-02-zhangsan 就业班-03-wangwu 就业班-04-wangwu 如果把学号和姓名封装成一个学生对象. 那么 预热班-许多学生对象 就业班-许多学生对象 (示意图) */ package map; import java.util.*; class Student{ private String id; private String name; Student(String id,String name){ this.id=id; this.name=name; } public String toString(){ return id+"..."+name; } } class MapExtend2{ public static void getStu(HashMap<String,List<Student>> hm){ //使用Map的keySet for(Iterator<String> it = hm.keySet().iterator();it.hasNext(); ){//遍历Map集合 String bj=it.next();//取出班级 for(Iterator<Student> itList=hm.get(bj).iterator();itList.hasNext();){//遍历取出的List集合 Student stu=itList.next(); System.out.println(bj+"..."+stu); } } } public static void main(String[] args){ HashMap<String,List<Student>> hm = new HashMap<String,List<Student>>();//Value的值为List集合对象 List<Student> list_1 = new ArrayList<Student>();//list存放学生对象 List<Student> list_2 = new ArrayList<Student>(); list_1.add(new Student("01","zhangsan")); list_1.add(new Student("02","wangwu")); list_2.add(new Student("03","maliu")); list_2.add(new Student("04","zhangsan")); hm.put("预热班",list_1); hm.put("就业班",list_2); getStu(hm);} }