zoukankan      html  css  js  c++  java
  • 5.集合(3)——Map集合

    Map用于保存具有映射关系的数据,因此Map集合里保存着两组值,一组值用于保存Map里的key,另外一组值用于保存Map里的value,key和value都可以是任何引用类型的数据。Map的key不允许重复,即同一个Map对象的任何两个key通过equals()方法比较总是返回false。

    key和value之间存在单向一对一关系,即通过指定的key,总能找到唯一的,确定的value。从Map中取出数据时,只要给出指定的key,就可以取出对应的value。

    Set和Map之间的关系非常密切,虽然Map中放的元素时key-value对,Set集合中放的元素是单个对象,但如果把key-value当成key的附属,key在哪里,value就在哪里,这样就可以像对待Set一样来对待Map了。事实上,Map提供了 一个Entry内部类来封装key-value对,而计算Entry存储时则只考虑了Entry封装的key,从Java源码来看,Java是线实现了Map,然后通过包装一个所有value都为null的Map就实现了Set集合。

    Map接口中定义了如下常用方法:

    void clear():删除Map对象中所有key-value对。

    boolean containsKey(Object key):查询Map中是否包含指定的key,如果包含则返回true。

    boolean containsValue(Object value):查询Map中是否包含一个或多个value,如果包含返回true.

    Set entrySet():返回Map中包含的key-value对所组成的Set集合,每个集合元素都是Map.Entry对象。

    Object get(Object key):返回指定key所对应的value;如果此Map中不包含该key,则返回null

    boolean isEmpty():查询该Map是否为空,如果为空返回true。

    Set keySet():返回该Map中所有key组成的Set集合

    Object put(Object key,Object value):添加一个key-value对,如果当前Map中已经有一个与该key相等的key-value对,则新的key-value对会覆盖原来的key-value对。

    void putAll(Map m):将指定的Map中的key-value对复制到本Map中。

    Object remove(Object key):删除指定key所对应的key-value对,如果从该Map中成功删除该key-value对,该方法返回true,否则返回false。

    Object remove(Object key):删除指定key所对应的key-value对,返回被删除key所关联的value,如果该key不存在,则返回null。

    int size():返回该Map里的key-value个数。

    Collection values():返回该Map里所有value组成的Collection。

    Map集合最典型的用法就是成对地添加、删除key-value对,接下来即可判断该Map中是否包含指定key,是否包含指定value,也可以通过Map提供的keySet()方法获取所有key组成的集合,进而遍历Map中所有的key-value对。 

    import java.util.*;
    
    public class MapTest{
        public static void main(String[] args) {
            Map<String,String> map = new HashMap<String,String>();
            map.put("海贼王","路飞");
            map.put("全职猎人","小杰");
            map.put("灌篮高手","樱木花道");
            map.put("龙珠","悟空");
    
            System.out.println(map);
    
            //根据key获取对应的value值
            for(String str : map.keySet()) {
                System.out.println(str + "->" +map.get(str));
            }
            
            //判断是否包含指定key和value
            System.out.println("包含路飞:"+map.containsValue("路飞"));
            System.out.println("包含火影忍者:"+map.containsKey("火影忍者"));
            
            //根据key移除key-value
            System.out.println(map.remove("龙珠"));
            System.out.println(map);
        }
    }

    测试结果:

    1.HashMap和Hashtable实现类

    HashMap和HashTable都是Map接口的典型实现类,它们之间的关系完全类似于ArrayList和Vector的关系:Hashtable是一个古老的Map实现类,它从JDK1.0起就已经出现了,当它出现时,Java还没有提供Map接口,所以它包含了两个繁琐的方法,即elements()和keys()。现在很少使用这两个方法。

    除此之外,Hashtable和HashMap存在两点典型区别。

    1)Hashtable是一个线程安全的Map实现,但HashMap是线程不安全的实现,所以HashMap比Hashtable的性能高一点;但如果有多个线程访问同一个Map对象时,使用Hashtable实现类会更好。

    2)Hashtable不允许使用null作为key和value,如果试图把null值放进Hashtable中,将会引发NullPointerException异常:但HashMap可以使用null作为key或value。

    由于HashMap里的key不能重复,所以HashMap里最多只有一个key-value对的key为null, 但可以有无数个key-value对的value为null。

    为了成功地在HashMap,Hashtable中存储、获取对象,用作key的对象必须实现hashCode()方法和equals()方法。

    与HashSet集合不能保证元素的顺序一样,HashMap、Hashtable也不能保证其中key-value对的顺序。类似于HashSet,HashMap,Hashtable判断两个key相等的标准也是:两个key通过equals()方法比较返回true,两个key的hashCode值也相等。

    与HashSet类似的是,如果使用可变对象作为HashMap,Hashtable的key,并且程序修改了作为key的可变对象,则也可能出现于HashSet类似的情形:程序再也无法准确访问到Map中被修改过的key。

    2.LinkedHashMap

    HashSet有一个LinkedHashSet子类,HashMap也有一个LinkedHashMap子类;LinkedHashMap也使用双向链表来维护key-value对的次序,该链表负责维护Map的迭代顺序,迭代顺序与key-value对的插入顺序一致。

    LinkedHashMap需要维护元素的插入顺序,因此性能略低于HashMap的性能;但因为它以链表来维护内部顺序,所以在迭代访问Map里的全部元素时会有较好的性能。

    import java.util.*;
    
    public class LinkedHashMapTest{
        public static void main(String[] args) {
            Map<String,String> map = new LinkedHashMap<String,String>();
            map.put("龙珠","鸟山明");
            map.put("幽游白书","富坚义博");
            map.put("海贼王","尾田荣一郎");
            map.forEach((key,value) -> System.out.println(key+"->"+value));
        }
    }

    测试结果:

    上面程序中最后一行使用Java8位Map新增的forEach()方法来遍历Map集合。编译,运行上面程序,即可看到LinkedHashMap的功能:LinkedHashMap可以记住key-value的添加顺序。

    3.使用Properties读写属性文件

    Properties类是Hashtable类的子类,正如它的名字所暗示的,该对象在处理属性文件时特别方便(Windows操作平台上的ini文件就是一种属性文件)。Properties类可以把Map对象和属性文件关联起来,从而可以把Map对象中的key-value对写入属性文件中,也可以把属性文件中的"属性名=属性值"加载到Map对象中,由于属性文件中的属性名,属性值只能是字符串类型,所以Properties里的key,value都是字符串类型。该类提供了如下三个方法来修改Properties里的key、value值。

    提示:Properties相当于一个key,value都是String类型的Map

    String getProperty(String key):获取Properties中指定属性名对应的属性值,类似Map的get(Object key)方法。

    String getProperty(String key,String defaultValue):该方法与前一个方法基本类似,该方法多一个功能,如果Properties中不存在指定key时,则该方法指定默认值。

    Object setProperty(String key,String value):设置属性值,类似于Hashtable的put()方法,返回旧的value值。

    除此之外,它还提供了两个读写属性文件的方法。

    void load(InputStream inStream):从属性文件(以输入流表示)中加载key-value对,把加载到的key-value对追加到Properties里。

    void store(OutputStream out,String comments):将Properties中的key-value对输出到指定的属性文件中。

    import java.util.*;
    import java.io.*;
    public class PropertiesTest{
        public static void main(String[] args) throws Exception{
            Properties prop = new Properties();
            prop.setProperty("username","Joey");
            prop.setProperty("password","7120078");
            //把Properties内容输出到文件中
            prop.store(new FileOutputStream("a.ini"),"comment line");
            
            Properties prop1 = new Properties();
            prop1.setProperty("hobby","comic");
            prop1.load(new FileInputStream("a.ini"));
            System.out.println(prop1);
        }
    }

    测试结果:

    上面程序示范了Properties类的用法。

    4.SortedMap接口和TreeMap实现类

    正如Set接口派生出SortedSet子接口,SortedSet接口有一个TreeSet实现类一样,Map接口也派生出一个SortedMap子接口,SortedMap接口也有一个TreeMap实现类。

    TreeMap就是一个红黑树数据结构,每个key-value队即作为红黑树的一个节点。TreeMap存储key-value对时,需要根据key对节点进行排序,TreeMap可以保证所有的key-value对处于有序状态。TreeMap也有两种排序方式。

    A>自然排序:TreeMap的所有key必须实现Comparable接口,而且所有的key应该是同一个类的对象,否则将会抛出ClassCastException异常。

    B>定制排序:创建TreeMap时传入一个Comparator对象,该对象负责对TreeMap中的所有key进行排序。采用定制排序时不要求Map的key实现Comparable接口。

    类似于TreeSet中判断两个元素相等的标准,TreeMap中判断两个key相等的标准是:两个key通过compareTo()方法比较返回0,TreeMap即认为这两个key是相等的。

    如果使用自定义类作为TreeMap的key,切想让TreeMap良好地工作,则重写该类的equals()方法,使得其和compareTo()方法保持一致的返回结果:两个key通过equals()比较返回true时,它们通过compareTo()比较应该返回0.如果compareTo()和equals()方法返回结果不一致,TreeMap与Map接口的规则就会冲突。

    注意:Set和Map的关系十分密切,Java源码就是先实现了HashMap,TreeMap等集合,然后通过包装一个所有的value都为null的Map集合实现了Set集合类。

     下面以自然排序为例,介绍TreeMap的基本用法。

    import java.util.*;
    class R implements Comparable{
        private int birthYear;
    
        public R(int birthYear) {
            this.birthYear = birthYear;
        }
    
        public String toString() {
            return "R[birthYear:"+birthYear+"]";
        }
    
        public boolean equals(Object obj) {
            if(obj == this)
                return true;
            if(obj != null && obj.getClass() == R.class) {
                R r = (R)obj;
                return r.birthYear == this.birthYear;
            }
            return false;
        }
    
        public int compareTo(Object obj) {
            R r = (R)obj;
            return this.birthYear > r.birthYear ? 1 : this.birthYear == r.birthYear ? 0 : -1;
        }
    }
    
    public class TreeMapTest{
        public static void main(String[] args) {
            TreeMap<R,String> tm = new TreeMap<R,String>();
            tm.put(new R(1990),"龙珠");
            tm.put(new R(1995),"海贼王");
            tm.put(new R(1998),"火影忍者");
            tm.put(new R(2010),"黑子的篮球");
            tm.put(new R(2001),"网球王子");
            //观察打印顺序
            System.out.println(tm);
            //返回该TreeMap的第一个Entry对象
            System.out.println(tm.firstEntry());
            //返回该TreeMap的最后一个key值
            System.out.println(tm.lastKey());
            //返回该TreeMap的比new R(1997)大的最小key值
            System.out.println(tm.higherKey(new R(1997)));
            //返回该TreeMap的比new R(1997)小的最大key-value对
            System.out.println(tm.lowerEntry(new R(1997)));
            //返回该TreeMap的子TreeMap
            System.out.println(tm.subMap(new R(1990),new R(2001)));
        }
    }

    测试结果:

    5.WeakHashMap实现类

    WeakHashMap与HashMap的用法类似,与HashMap的区别在于,HashMap的key保留了实际对象的强引用,这意味着只要该HashMap对象不被销毁,该HashMap的所有key所引用的对象就不会被垃圾回收,HashMap也不会自动删除这些key所对应的key-value对;但WeakHashMap的key只保留了对实际对象的弱引用,这意味着如果WeakHashMap对象的key所引用的对象没有被其他强引用变量所引用,则这些key所引用的对象可能被垃圾回收,WeakHashMap也可能自动删除这些key所对应的key-value对。

    WeakHashMap中的每个key对象都只持有对实际对象的弱引用,因此,当垃圾回收了该key所对应的实际对象之后,WeakHashMap会自动删除该key对应的的key-value对。

    import java.util.*;
    
    public class WeakHashMapTest{
        public static void main(String[] args) {
            WeakHashMap<String,String> whm = new WeakHashMap<String,String>();
            //向WeakHashMap中添加三个key-value对
            //有两个个key都是匿名字符串(没有其他引用)
            whm.put(new String("名侦探柯南"),"青山刚昌");
            whm.put(new String("游戏王"),"高桥和希");
            whm.put("全职猎人","富坚义博");
            //向WeakHashMap中添加一个key-value队
            //该key是一个系统缓存的字符串对象
            whm.put("java","change the world");
            System.out.println(whm);
            //通知系统立即进行垃圾回收
            System.gc();
            System.runFinalization();
            //再次打印集合中元素
            System.out.println(whm);
        }
    }

    测试结果:

    从上面运行结果可以看出,当系统进行垃圾回收时,删除了WeakHashMap对象的前两个key-value对,这是因为添加这两个key-value对时,这两个key都是匿名的字符串对象,WeakHashMap只保留了对它们的弱引用,这样垃圾回收时会自动删除这两个key-value对。

    WeakHashMap对象中后两个key-value对的key是字符串直接量,(系统会自动保存对该字符串对象的强引用),所以垃圾回收时不会回收它。

    6.IdentityHashMap实现类

    这个实现类的实现机制与HashMap基本相似,但它在处理两个key相等时比较独特:在IdentityHashMap中,当且仅当两个key严格相等(key1==key2)时,IdentityHashMap才认为两个key相等:对于普通的HashMap而言,只要key1和key2通过equals()方法比较返回true,且它们的ahshCode值相等即可。

    7.EnumMap实现类

    EnumMap是一个和枚举类一起使用的Map实现,EnumMap中的所有key都必须是单个枚举类的枚举值。创建EnumMap时必须显式或隐式指定它对应的枚举类。EnumMap具有如下特征:

    EnumMap在内部以数组形式保存,所以这种实现形式非常紧凑,高效。

    EnumMap根据key的自然顺序来维护key-value对的顺序。

    与创建普通的Map有所区别的是,创建EnumMap时必须指定一个枚举类,从而将该EnumMap和指定枚举类关联起来。

    import java.util.*;
    
    enum Season {
        SPRING,SUMMER,FALL,WINTER
    }
    
    public class EnumMapTest{
        public static void main(String[] args) {
            EnumMap em = new EnumMap(Season.class);
            em.put(Season.SUMMER,"夏日炎炎");
            em.put(Season.SPRING,"春暖花开");
            System.out.println(em);
        }
    }

    测试结果:

    上面程序创建了一个EnumMap对象,创建该对象时指定它的key只能是Season枚举类的枚举值。如果向该EnumMap中添加两个key-value对后,这两个key-value对将会以Season枚举值的自然顺序排序。

    8.各Map实现类的性能分析

    对于Map的常用实现类而言,虽然HashMap和Hashtable的实现机制几乎一样,但由于Hashtable是一个古老、线程安全的集合,因此HashMap通常比Hashtable要快。

    TreeMap通常比HashMap,Hashtable要慢,因为TreeMap底层采用红黑树来管理key-value对。使用TreeMap有一个好处:TreeMap中的key-value总是处于有序状态,无须专门进行排序操作。

    对于一般的场景,程序应该多考虑使用HashMap,因为HashMap正是为快速查询设计的,但是如果程序需要一个总是排好序的Map时,则可以考虑使用TreeMap。

     9.操作集合的工具类:Collections

    Java提供了一个操作Set、List和Map等集合的工具类:Collections,该工具类里提供了大量方法对集合元素进行排序、查询和修改等操作,还提供了将集合对象设置为不可变,对集合对象实现同步控制等方法。

    排序操作

    void reverse(List list):反转指定List集合中元素的顺序。

    void shuffle(List list):对List集合元素进行随机排序(shuffle方法模拟了“洗牌”动作).

    void sort(List list):根据元素自然顺序对指定List集合的元素按升序进行排序。

    void sort(List list,Comparator c):根据hidingComparator产生的顺序对List集合元素进行排序

    void swap(List list,int i,int j):将指定List集合中的i处元素和j处元素进行交换

    void rotate(List list,int distance):当distance为正数时,将list集合的后distance个元素"整体"移到前面;当distance为负数时,将list集合的前distance个元素"整体"移到后面。该方法不会改变集合的长度。

    下面程序示范了Collections类常用的排序操作

    import java.util.*;
    
    public class SortTest{
        public static void main(String[] args) {
            List<Integer> nums = new ArrayList<Integer>();
            nums.add(1);
            nums.add(9);
            nums.add(9);
            nums.add(5);
            System.out.println(nums);
            //排序
            Collections.sort(nums);
            System.out.println(nums);
            //反转
            Collections.reverse(nums);
            System.out.println(nums);
            //打乱成随机顺序
            Collections.shuffle(nums);
            System.out.println(nums);
        }
    }

    测试结果:

  • 相关阅读:
    .net core读取appsettings.config中文乱码问题
    vs2017错误:当前页面的脚本发生错误
    VS Code中无法识别npm命令
    Visual Studio报错/plugin.vs.js,行:1074,错误:缺少标识符、字符串或数字
    记录一次在生成数据库服务器上出现The timeout period elapsed prior to completion of the operation or the server is not responding.和Exception has been thrown by the target of an invocation的解决办法
    Java集合框架
    java hash表
    Java Dictionary 类存储键值
    java数据结构 栈stack
    java封装
  • 原文地址:https://www.cnblogs.com/blogforvi/p/9366357.html
Copyright © 2011-2022 走看看