Map集合学习

来源:互联网 发布:数据库如何打开mdf文件 编辑:程序博客网 时间:2024/06/08 03:45

Map集合学习

 

备注:Map:一次添加一对元素。Collection一次添加一个元素。

一、Map说明

       Map也称为双列集合,Collection集合称为单列集合。

       其实map集合中存储的就是键值对。

       map集合中必须保证键的唯一性。

Map集合不具备迭代器。迭代器只有Collection系列的集合才有

二、Map接口的常用方法:

1,添加。

       value put(key,value):返回前一个和key关联的值(上一个key对象的value值),如果没有返回null.

2,删除。

       void clear():清空map集合。

       value remove(key):根据指定的key翻出这个键值对。

3,判断。

       boolean containsKey(key):

       boolean containsValue(value):

       boolean isEmpty();

4,获取。

       value get(key):通过键获取值,如果没有该键返回null。当然可以通过返回null,来判断是否包含指定键。

       int size(): 获取键值对的个数。

三、Map接口常用的实现类:

       |--Hashtable :内部结构是哈希表,是同步的。不允许null作为键,不允许null作为值。

              |--Properties:用来存储键值对型的配置文件的信息,可以和IO技术相结合。             

       |--HashMap : 内部结构是哈希表,不是同步的。允许null作为键,允许null作为值。

      |--LinkHashMap:内部结构是哈希表和链接列表实现,具有可预知的迭代顺序(有序的)

       |--TreeMap : 内部结构是二叉树,不是同步的。可以对Map集合中的键进行排序

四、set集合和map集合关系

备注:

学习Set集合应该注意:Set集合的底层代码是由Map集合实现的。

为什么会有set集合?

因为:Map的集合中存储的是键值对,其中键在Map集合中是唯一的,因此将Map中的键作为set中的元素进行存储,就可以保证单列集合的唯一性了,这样就出现了没有重复元素的set集合了。

因为现实世界中因为有这样的需求,所以就有了set集合的存在

推论:

因为Set集合的底层代码是由Map集合实现的,所以set的子类xxxSet的底层实现是Map的子类xxxMap。

而我查看java的源码发现,这个推论在一定程度上是成立的。因为我发现:1)HashMap是HashSet的底层实现。2)TreeMap是TreeSet的底层实现。3)LinkedHashMap是LinkedHashSet的底层实现

1、HashMap是HashSet的底层实现

    public HashSet() {        map = new HashMap<>();}

2、TreeMap是TreeSet的底层实现

    public TreeSet() {        this(new TreeMap<E,Object>());    }

3、LinkedHashMap是LinkedHashSet的底层实现

    public LinkedHashSet() {        super(16, .75f, true);    }    HashSet(int initialCapacity, float loadFactor, boolean dummy) {        map = new LinkedHashMap<>(initialCapacity, loadFactor);    }

五、map遍历方法

1、方法一:    Set<K> keySet()方法

取出map中的所有元素。

原理,通过keySet方法获取map中所有的键所在的Set集合,在通过Set的迭代器获取到每一个键,

在对每一个键通过map集合的get方法获取其对应的值即可。


思考:为什么是返回Set集合,而不是返回List集合呢?

map中的key是唯一的,而set集合有一个特点:元素是唯一的,所以返回set集合符合了mapkey的特点,因此使用set集合。而List集合的元素则不保证集合元素的唯一性,所以没有考虑使用List集合。此处的选择,目的只有一个:确保拿到的key集合是唯一的,不重复的。

2、方法二: Set<Map.Entry<K,V>>entrySet()方法

通过Map转成set就可以迭代。

找到了另一个方法:entrySet。

该方法将键和值的映射关系作为对象存储到了Set集合中,而这个映射关系的类型就是Map.Entry类型

 

3、Map.Entry<K,V>之思

备注:内部类,内部接口需要进一步学习

(1)Map接口与Map.Entry接口

Map接口:外部接口(外部规则)

Map.Entry接口:内部接口(内部规则)--包含键和值的内容,是键和值的映射关系对象

(2)先后关系

先有Map接口中的Map(键值对)的映射,才有映射关系,而Entry接口做的就是把这个关系封装成了对象,且这个关系是访问Map中的键和值。

(3)外部规则与内部规则

外部规则里面有内部规则,内部规则可以直接访问内部规则的内部

(4)示例代码:

//内部接口:MyEntry 外部接口:MyMapinterface MyMap{       public static interface MyEntry{//内部接口              void get();       }} class MyDemoimplements MyMap.MyEntry{       public void get(){}}//内部类:Innnerclass Outer{       static class Inner{              static void show(){}       }}

4、方法三:values方法

原理:取出Map集合中的所有值的Collection集合

5、三种方法的集合遍历案例

(1)代码实现

package map;/* * map集合遍历 * 1.遍历方法一:KeySet方法 * 2.遍历方法二:Map.Entry接口的 entrySet方法 * 3.遍历方法三:values方法 */importjava.util.Collection;importjava.util.HashMap;importjava.util.Iterator;importjava.util.Map;importjava.util.Map.Entry;importjava.util.Set;public class MapDemo {    public static void main(String[] args) {//     Map map=new HashMap();       Map<Integer,String> map=new HashMap<Integer,String>();//泛型       map.put(1, "张三");       map.put(2, "李四");       map.put(3, "王五");       map.put(4, "世间");       map.put(5, "时间");       //1.遍历方法一:KeySet方法       Set<Integer> keySet=map.keySet();//拿到键的集合       Iterator<Integer> it=keySet.iterator();//迭代器       System.out.println("遍历方法一:KeySet方法");       while(it.hasNext()){           int key=(int) it.next();           String value=map.get(key);           System.out.println(key+":"+value);       }       //2.遍历方法二:Map.Entry接口的 entrySet方法       Set<Map.Entry<Integer,String>> bothResult=map.entrySet();//返回map集合中包含的键和值的映射关系       Iterator<Entry<Integer,String>>it2=bothResult.iterator();//迭代器       System.out.println("遍历方法二:Map.Entry接口的 entrySet方法");       while(it2.hasNext()){           Entry<Integer,String> entry=(Entry<Integer, String>) it2.next();           int key=entry.getKey();           String value=entry.getValue();           System.out.println(key+"::"+value);       }       //3.遍历方法三:values方法       Collection<String> cols=map.values();       Iterator<String> it3=cols.iterator();       System.out.println("遍历方法三:values方法");       while(it3.hasNext()){           String value=it3.next();           System.out.println(value);       }    }}

(2)结果

遍历方法一:KeySet方法

1:张三

2:李四

3:王五

4:世间

5:时间

遍历方法二:Map.Entry接口的 entrySet方法

1::张三

2::李四

3::王五

4::世间

5::时间

遍历方法三:values方法

张三

李四

王五

世间

时间

 

六、自定义对象存map集合,map集合保证键唯一性探讨

联系set集合

set集合中的TreeSet和HashSet中都有保证存入元素唯一的方法,而map集合如何保证存入map集合的键(key)唯一呢?

因为set集合的底层是通过map集合实现。

而且我们知道:

TreeMap是TreeSet的底层实现

HashMap是HashSet的底层实现

所以保证唯一,通过类比的方式得出:

HashMap(保证键唯一)与HashSet(保证元素唯一)方式一样

是通过对象的hashCode和equals方法来完成对象唯一性的。

 

TreeMap(保证键唯一)与TreeSet(保证元素唯一)方式一样

实现Comparable接口的compareTo方法 或者 实现 Comparator接口,覆盖compare方法

七、HashMap集合

1、HashMap集合说明

HashMap : 内部结构是哈希表,不是同步的。允许null作为键,允许null作为值。

2、HashMap集合保证键(key)唯一

HashMap是HashSet的底层实现

所以保证唯一,通过类比的方式得出:

HashMap(保证键唯一)与HashSet(保证元素唯一)方式一样

是通过对象的hashCode和equals方法来完成对象唯一性的。

3、自定义对象与非自定义对象之HashMap集合使用

(1)非自定义对象:

  HashMap集合中存储非自定义对象,即:jdk中已有的对象(比如:String对象,Integer对象),这些对象覆盖了Object对象的hashCode方法和equals方法,可以保证这些对象插入HashMap集合时,键值是唯一的。

(2)自定义对象:

HashMap集合中存储自定义对象,即:自己写的对象。我们要想保证这样的自定义对象作为键时,键值是唯一的,我们需要给这个自定义对象覆盖Object对象的hashCode方法和equals方法。这样,才可以

可以保证这些自定义对象插入HashMap集合时,键值是唯一的。

4、HashMap案例

4.1案例一:将学生对象和学生的归属地通过键与值存储到map集合

(1)自定义对象Student类

class Student{    private String name;    private int age;       public Student(String name, int age) {       this.name = name;       this.age = age;    }    public String getName() {       return name;    }    public void setName(String name) {        this.name = name;    }    public int getAge() {       return age;    }    public void setAge(int age) {       this.age = age;    }    @Override    public int hashCode() {       //乘以一些奇数,增大随机性       return this.name.hashCode()+age*37;    }       /*     * 1.比较对象内容是否相同     * 2.健壮性判断(1)对象地址是否相同,相同则忽略比较(2)对象类型是否正确,错误则抛出异常     */    @Override    public boolean equals(Object obj) {             if(this==obj){           return true;//同一个学生       }       if(!(obj instanceof Student)){           throw new RuntimeException("类型错误");       }       //姓名和年龄都相同的视为同一个学生       Student s=(Student)obj;       //同一个学生       if(this.getName().equals(s.getName())&&this.getAge()==s.getAge()){           return true;       }       return false;//不是同一个学生    }   }

(2)HashMap测试类

package map; importjava.util.HashMap;importjava.util.Map;importjava.util.Set; import list_set.People; /* * 将学生对象和学生的归属地通过键与值存储到map集合中 * * 判断相同依据:姓名和年龄都相同的视为同一个学生 * * HashMap 类比   HashSet * 保证唯一:是通过对象的hashCode和equals方法来完成对象唯一性的 */public class HashMapDemo {     public static void main(String[] args) { //     HashMap hm=new HashMap<Student,String>();       HashMap<Student,String> hm=new HashMap<Student,String>();       hm.put(new Student("张三",20),"杭州");       hm.put(new Student("张三",20),"黑龙江");//和上面的键重复了,测试键是否插入唯一       //结果:只有一个new Student("张三",20)作为键,说明使用成功了       hm.put(new Student("李四",20),"广州");       hm.put(new Student("王五",20),"天津");       hm.put(new Student("李六",20),"北京");       hm.put(new Student("世间",20),"上海");       //遍历       Set<Map.Entry<Student,String>>set=hm.entrySet();       for(Map.Entry<Student, String> map:set){           Student s=map.getKey();           String name=s.getName();//名字           int age=s.getAge();//年龄           String area=map.getValue();//归属地           System.out.println(name+":"+age+":"+area);       }    } }

(3)结果:(保证了自定义对象的键唯一)

王五:20:天津

张三:20:黑龙江

世间:20:上海

李四:20:广州

李六:20:北京

 

4.2案例二:map对象存jdk已有对象

(1)测试类

package map;importjava.util.HashMap;importjava.util.Set;public class HashMapDemo2 {    public static void main(String[] args) {       HashMap<Integer,String>  hm=new HashMap<Integer,String>();       hm.put(1, "张三");       hm.put(1, "张三");//重复键,覆盖原来的值       hm.put(2, "张三3");       hm.put(3, "张三5");       Set<Integer> set=hm.keySet();       for(Integer i:set){           int key=i;           String value=hm.get(key);           System.out.println(i+":"+value);       }    }} 

(2)结果

Integer对象已经覆盖了Object对象的equalshashCode方法,我们直接把Integer对象作为map集合的键插入集合中即可保证键的唯一性,无需自己做额外的操作

1:张三

2:张三3

3:张三5

八、TreeMap集合

1、TreeMap集合说明

TreeMap : 内部结构是二叉树,不是同步的。可以对Map集合中的键进行排序

 

2、TreeMap集合保证键(key)唯一

TreeMap是TreeSet的底层实现

所以保证唯一,通过类比的方式得出:

TreeMap(保证键唯一)与TreeSet(保证元素唯一)方式一样

实现Comparable接口的compareTo方法 或者 实现 Comparator接口,覆盖compare方法

3、自定义对象与非自定义对象之TreeMap集合使用

(1)TreeMap集合排序方式一:让元素自身具备比较功能

【1】当元素为自定义对象(eg:Person类)时,让元素自身具备比较功能,实现Comparable接口,覆盖compareTo方法

【2】当元素为字符串对象(eg:“abd”)或者jdk中的对象有实现Comparable接口的方法时,由于这些对象本身已经实现了Comparable接口的compareTo方法,本身就具备比较功能,直接使用TreeMap即可。

(2)TreeMap集合排序方式二:让集合自身具备比较功能

集合自身具备比较功能,定义一个类实现Comparator接口,覆盖compare方法。将该类对象作为参数传递给TreeMap集合的构造函数。

(3)排序优先

当TreeMap集合有两种比较功能时,TreeMap集合排序方式二(集合自身具备比较功能优先于 TreeMap集合排序方式一(元素本身具备比较功能

4、TreeMap案例—按自定义对象的姓名属性排序

(1)实现方式一:

TreeMap集合排序方式一:让元素自身具备比较功能

【1】自定义对象XueSheng(实现Comparable接口)

package map; //学生对象public class XueSheng implements Comparable{    private String name;    private int age;       public XueSheng(String name, int age) {       this.name = name;       this.age = age;    }    public String getName() {       return name;    }    public void setName(String name) {       this.name = name;    }    public int getAge() {       return age;    }    public void setAge(int age) {       this.age = age;    }       @Override    public int compareTo(Object o) {       XueSheng s=(XueSheng)o;       //按自定义对象的姓名属性排序       return this.getName().compareTo(s.getName());    }   }

【2】TreeMap测试类

package map; importjava.util.Map;importjava.util.Set;importjava.util.TreeMap; /* * 将学生对象和学生的归属地通过键与值存储到map集合中 * * 按自定义对象的姓名属性排序 * TreeMap集合排序方式一:让元素自身具备比较功能 */public class TreeMapDemo {    public static void main(String[] args) {        TreeMap<XueSheng,String> tm=new TreeMap<XueSheng,String>();             tm.put(new XueSheng("张三",20),"杭州");       tm.put(new XueSheng("张三",20),"黑龙江");//和上面的键重复了,测试键是否插入唯一       //结果:只有一个new XueSheng("张三",20)作为键,说明使用成功了       tm.put(new XueSheng("李四",20),"广州");       tm.put(new XueSheng("王五",20),"天津");       tm.put(new XueSheng("李六",20),"北京");       tm.put(new XueSheng("世间",20),"上海");       //遍历       Set<Map.Entry<XueSheng,String>>set=tm.entrySet();             for(Map.Entry<XueSheng, String> map:set){           XueShengs=map.getKey();           String name=s.getName();//名字           int age=s.getAge();//年龄           String area=map.getValue();//归属地           System.out.println(name+":"+age+":"+area);       }    }}

【3】结果:(保证了自定义对象的键唯一)

世间:20:上海

张三:20:黑龙江

李六:20:北京

李四:20:广州

王五:20:天津

(2)实现方式二:

TreeMap集合排序方式二:让集合自身具备比较功能

【1】自定义对象XueSheng

package map; //学生对象public class XueSheng{    private String name;    private int age;       public XueSheng(String name, int age) {       this.name = name;       this.age = age;    }    public String getName() {       return name;    }    public void setName(String name) {       this.name = name;    }    public int getAge() {       return age;    }    public void setAge(int age) {       this.age = age;    }  }

【2】ComparatorByName比较器类

package map; importjava.util.Comparator;//比较器//实现 按自定义对象的姓名属性排序public class ComparatorByName implements Comparator{     @Override    public int compare(Object o1, Object o2) {       XueSheng s1=(XueSheng)o1;       XueSheng s2=(XueSheng)o2;             // 按自定义对象的姓名属性排序       return s1.getName().compareTo(s2.getName());    } }

【3】TreeMap测试类

package map; importjava.util.Map;importjava.util.Set;importjava.util.TreeMap; /* * 将学生对象和学生的归属地通过键与值存储到map集合中 * * 按自定义对象的姓名属性排序 * TreeMap集合排序方式二:让集合自身具备比较功能 */public class TreeMapDemo2 {     public static void main(String[] args) {        TreeMap<XueSheng,String> tm=new TreeMap<XueSheng,String>(new ComparatorByName()); //比较器的使用       tm.put(new XueSheng("张三",20),"杭州");       tm.put(new XueSheng("张三",20),"黑龙江2");//和上面的键重复了,测试键是否插入唯一       //结果:只有一个new XueSheng("张三",20)作为键,说明使用成功了       tm.put(new XueSheng("李四",20),"广州");       tm.put(new XueSheng("王五",20),"天津");       tm.put(new XueSheng("李六",20),"北京");       tm.put(new XueSheng("世间",20),"上海");       //遍历       Set<Map.Entry<XueSheng,String>>set=tm.entrySet();             for(Map.Entry<XueSheng, String> map:set){           XueSheng s=map.getKey();           String name=s.getName();//名字           int age=s.getAge();//年龄           String area=map.getValue();//归属地           System.out.println(name+":"+age+":"+area);       }    } }

【4】结果:

世间:20:上海

张三:20:黑龙江2

李六:20:北京

李四:20:广州

王五:20:天津

九、LinkedHashMap集合(有序)

LinkHashMap:【父类是HashMap内部结构是哈希表和链接列表实现,具有可预知的迭代顺序(有序的)

联系set集合

LinkedHashSet的底层是由LinkedHashMap实现的。

说明:

如果要保证LinkedHashMap集合键的唯一性,可以参考父类HashMap的实现方式,它们俩实现是一样的。

案例

代码实现:

package map; importjava.util.Iterator;importjava.util.LinkedHashMap;importjava.util.Map;importjava.util.Map.Entry;/* * 有序   插入顺序和遍历获取顺序一致 */public class LinkedHashMapDemo {     public static void main(String[] args) {        LinkedHashMap<String,Integer> lhm=new LinkedHashMap<String,Integer>();       lhm.put("张三", 1023);       lhm.put("lisi", 1021);       lhm.put("王五", 1022);       lhm.put("你好", 1025);       lhm.put("世间", 1026);       Iterator<Map.Entry<String,Integer>>it= lhm.entrySet().iterator();       while(it.hasNext()){           Entry<String,Integer> ele=it.next();           String key = ele.getKey();           Integer value = ele.getValue();           System.out.println(key+":"+value);       }    } }

结果:(有序)

张三:1023

lisi:1021

王五:1022

你好:1025

世间:1026

 

十、实现功能--记录字母次数

代码实现:

package map; importjava.util.Iterator;importjava.util.Map.Entry;importjava.util.TreeMap; /* * 记录字母次数 * *  字符串String s="kasdgasdvoasdfnasgsdgaegbweeegwef",获取该字符串中,每一个字母出现的次数。 * 要求打印结果是:a(5)b(1)...; * * 分析: * 1、记录结果包括字母和次数,确定容器为map集合,以键值对形式存储 * 2、因为输出结果是按照字母的字典顺序输出的,所以选择使用TreeMap集合 * 3、该实现特点:字母(key)不变,次数(value)不断变化 * 实现思路: * 1、key  是字母  value  是次数 * 2、将字符串转为字符数组,然后给字符数组进行遍历 * 3、遍历字符数组时,分两种情况考虑: * (1)当map集合中不存在当前字母(key),即把当前字母存入,并设置其value值为1【表示第一次出现】 * (2)当map集合存在当前字母(key),取出该字母对应的value,进行加1(value=value+1)操作【表示又一次出现】 * 4、进行遍历输出map集合中的key和value即可 */public class CountByCharacter {     public static void main(String[] args) {        String s="12kasdgasdv44oasdfnasgsdgaegbweeegwef";             char[] sArray=s.toCharArray();//字符串变字符数组             TreeMap<String,Integer> tm=arrayToMap(sArray);//计算字母出现次数,并将字母和次数保存在map集合中             listCount(tm);//遍历输出字符串中的字母和次数情况    }     //计算字母出现次数,并将字母和次数保存在map集合中    private static TreeMap<String, Integer>arrayToMap(char[] sArray) {       TreeMap<String,Integer> tm=new TreeMap<String,Integer>();       //遍历字符数组       for(int i=0;i<sArray.length;i++){                     char letter=sArray[i];           //健壮性判断,key必须是字母,否则不进行存储和计算           boolean result=keyIsCharacter(letter);           if(!result){              continue;//退出该次循环,直接进入下一次循环           }           //键           String key=letter+"";//字符letter转为String类型                     //默认情况设置为1(默认情况:key第一次出现)           int value=1;//value=1                     //key第二次出现           if(tm.get(key)!=null){              //value=value+1              value=tm.get(key)+1;//tm.get(key)就是上一个value值           }           //key是char类型,需要转为String类型:key+""           tm.put(key, value);//设置key和value       }       return tm;    }     //判断key是否是字母,如果是,返回true;否则返回false    private static boolean keyIsCharacter(char key) {             //key是字母       if((key>='A'&&key<='Z')||(key>='a'&&key<='z'))           return true;       return false;    }     //遍历输出字符串中的字母和次数情况    private static void listCount(TreeMap<String, Integer> tm) {        Iterator<Entry<String,Integer>>it=tm.entrySet().iterator();       while(it.hasNext()){           Entry<String,Integer> entry=it.next();           String key=entry.getKey();           int value=entry.getValue();           System.out.print(key+"("+value+") ");       }    }}


结果:

a(5) b(1) d(4)e(5) f(2) g(5) k(1) n(1) o(1) s(5) v(1) w(2)

结论:

不同容器有不同的应用场景,在不同情况下,有些容器实现起来极其简单和高效,所以我们要努力挖掘出该应用场景下的关键信息,然后做出最佳选择

原创粉丝点击