zoukankan      html  css  js  c++  java
  • 7.6 增强的Map集合


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

    Map里的所有key就放在一起,它们就组成一个Set集合(所以key没有顺序,key与key之间不能重复),实际上Map确实包含一个keySet()方法,用于返回Map里所有key组成的Set集合。
    不仅如此,Map里key集和Set集合元素的存储形式也很像,Map子类和Set子类在名字上也很像,比如Set接口中有HashSet、LinkedSet、sorted接口、TreeSet、EnumSet等子接口和实现类,而Map接口下则有HashMap、LinkedHashMap、SortedMap接口、TreeMap、EnumMap等子接口和实现类。Map的这些实现类和子接口中key集的存储形式和对应的Set集合中元素的存储形式相同。
    如果把Map里的所有value放在一起来看,它们又非常类似一个List:元素与元素之间可以重复,每个元素可以根据索引来查找,只是Map中的索引不在使用整数值,而是以另一个对象作为索引。,因此Map有时也成为字典或关联数组。

    一、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(Entry是Map的内部类)对象。
    ★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对,并返回被覆盖的value。
    ★void putAll(Map m):将指定Map中的key-value对复制到本Map中。
    ★Object remove(Object key):删除指定的key所对应的key-value对,返回被删除的key所关联的value,如果该key不存在,则返回null。
    ★boolean remove(Object key,Object value):Java 8新增的方法,删除指定的key、value所对应的key-value对。如果成功从该Map中成功删除该key-value对,则返回true,否则返回false。
    ★int size():返回该Map种key-value对的个数。
    ★Collection values():返回该Map种所有value值组成的Collection。
    Map接口提供了大量的实现类,典型实现如HashMap和Hashtable等、HashMap的子类LinkedHashMap,还有SortedMap子接口和该接口的实现类TreeMap,以及WeakHashMap、IdentityHashMap等。
    Map中包含一个内部类Entry,该类封装了一个key-value对。Entry包含三个方法:
    1、Object getKey():返回该Entry里所包含的key值。
    2、Object getValue():返回该Entry里所包含的value值。
    3、Object setValue(V value):设置该Entry里包含的value值,并返回新的value值。

    import java.util.HashMap;
    import java.util.Map;
    public class MapTest 
    {
    	public static void main(String[] args) 
    	{
    		var map=new HashMap();
    		//成对放入多个key-value对
    		map.put("疯狂Java讲义",109);
    		map.put("疯狂iOS讲义",10);
    		map.put("疯狂Ajax讲义",79);
    		map.put("轻量级Java EE企业应用实战",99);
    		//多次放入重复的key时,新的value会覆盖原来的value
    		System.out.println(map.put("疯狂iOS讲义",99));//10
    
    		System.out.println(map);//{疯狂Ajax讲义=79, 疯狂iOS讲义=99, 轻量级Java EE企业应用实战=99, 疯狂Java讲义=109}
    
    		//判断是否包含指定key
    		System.out.println("判断是否包含值为 "疯狂iOS讲义"的key:"+map.containsKey("疯狂iOS讲义"));//true
    		//判断是否包含指定的value
    		System.out.println("是否包含值为99的value:"+map.containsValue(99));//true
    		
    		//获取Map集合的所有的key组成的集合,通过遍历key来实现遍历所有的key-value对
    		System.out.println(map.keySet());
    		for(var key:map.keySet())
    		{
    			System.out.println(key+"-->"+map.get(key));
    		}
    
    		//根据key来删除key-value对
    		map.remove("疯狂Ajax讲义");
    		System.out.println(map);//{疯狂iOS讲义=99, 轻量级Java EE企业应用实战=99, 疯狂Java讲义=109}
    
    	}
    }
    ---------- 运行Java捕获输出窗 ----------
    10
    {疯狂Ajax讲义=79, 疯狂iOS讲义=99, 轻量级Java EE企业应用实战=99, 疯狂Java讲义=109}
    判断是否包含值为 "疯狂iOS讲义"的key:true
    是否包含值为99的value:true
    [疯狂Ajax讲义, 疯狂iOS讲义, 轻量级Java EE企业应用实战, 疯狂Java讲义]
    疯狂Ajax讲义-->79
    疯狂iOS讲义-->99
    轻量级Java EE企业应用实战-->99
    疯狂Java讲义-->109
    {疯狂iOS讲义=99, 轻量级Java EE企业应用实战=99, 疯狂Java讲义=109}
    
    输出完成 (耗时 0 秒) - 正常终止
    

    HashMap重写了toString()方法,实际上所有的Map都重写了toString()方法,调用Map对象的toString()方法总是会返回如下格式的字符串:{key1=value1,key2=value2...}

    二、Java 8 为Map新增的方法

    Java 8除了为Map增加了remove(Object key,Object value)默认的方法之外,还增加了以下方法。

    方法 描述
    Object compute(object key,BiFunction remappingFunction) 该方法使用remappingFunction根据原key-value对计算一个新的value。只要新的value不为null,就是用新的value覆盖原来的value;如果原value不为null,但新的value为null,则删除原key-value对;如果原value、新value同时为null,那么该方法不改变任何key-value对
    Object computeIfAbsent(object key,BiFunction remappingFunction) 如果传给该方法的key参数在Map中对应的value为null,则使用mappingFunction根据key计算一个新的结果,如果计算结果不为null,则用计算结果覆盖原来的value。如果原Map不包含该key,那么该方法可能会添加一组key-value对
    Object computeIfPresent(Object key,object key,BiFunction remappingFunction) 如果传给该方法的key参数在Map中对应的value不为null,则使mappingFunction根据key、value计算一个新的结果,如果计算结果不为null,则用计算结果覆盖原来的value。如果计算结果为null,则删除原key-value对
    forEach(BiConsumer action) Java 8为Map新增的一个遍历key-value对的方法,通过该方法可以更加简洁的遍历map的key-value对
    Object getOrDefault(Object key,V defaultValue) 获取指定key对应的value,如果该key不存在,则返回defaultValue
    Object merge(Object key,Object value,BiFunction remappingFunction) 现根据key参数获取该Map对应的value。如果value为null,则直接用传入的value覆盖原有value(在这种情况下,可能要添加一组key-value对);如果获取的value不为null,则使用remappingFunction根据根据原value、新value计算一个新的结果,并用该结果去覆盖原来的value
    Object putIfAbsent(Object key,Object value) 该方法会自动检测指定key对应的value是否为null,如果为null,则用新value值代替原来的value
    Object replace(Object key, Object value) 将map中指定key对应的value替换成新的value。与传统put()方法不同,该方法不可能添加新的key-value对。如果尝试替换的key在原Map中不存在,直接返回null
    boolean replace(K key,V oldValue,V newValue) 将Map中指定的key-value对的value替换成新的value。如果在Map中找到key-value对,则执行操作,并返回true,否则返回false
    replaceAll(BiFunction function) 该方法使用BiFunction对原key-value对执行计算,并将计算结果作为该key-vlaue对的value

    Java 8新增用法示例:

    import java.util.HashMap;
    public class HashMapTest2 
    {
    	public static void main(String[] args) 
    	{
    		var map=new HashMap();
    		//成对放入多个key-value对
    		map.put("疯狂Java讲义",109);
    		map.put("疯狂iOS讲义",99);
    		map.put("疯狂Ajax讲义",79);
    
    		//尝试替换key为"疯狂XML讲义"的value,由于原Map中没有对应的key
    		//因此Map没有改变,不会添加新的key-value对
    		map.replace("疯狂XML讲义",66);
    		System.out.println(map);//{疯狂Ajax讲义=79, 疯狂iOS讲义=99, 疯狂Java讲义=109}
    
    		//使用原value与传入参数计算新的结果覆盖原有的value
    		map.merge("疯狂iOS讲义",10,
    			(oldVal,newVal)->(Integer)oldVal+(Integer)newVal);
    			System.out.println(map);//{疯狂Ajax讲义=79, 疯狂iOS讲义=109, 疯狂Java讲义=109}
    			
    		//当key为"Java"对应的value为null(或不存在)时,使用计算结果作为新的value
    		map.computeIfAbsent("Java",key->((String)key).length());
    		System.out.println(map);//{Java=4, 疯狂Ajax讲义=79, 疯狂iOS讲义=109, 疯狂Java讲义=109}
    		map.computeIfPresent("Java",(key,value)->(Integer)value+((String)key).length());
    		System.out.println(map);//{Java=8, 疯狂Ajax讲义=79, 疯狂iOS讲义=109, 疯狂Java讲义=109}
    	}
    }
    

    三、改进的HashMap和Hashtable实现类


    HashMap和Hashtable都是Map接口的典型实现类,它们之间的关系类似于ArrayList和Vector的关系:Hashtable是一个古老的Map实现类,包含了了两个比较繁琐的方法,即elements()和keys()。但是尽量减少使用Hashtable。
    Java 8改进了HashMap的实现,使用HashMap存在key冲突时依然具有较好的性能。
    此外Hashtable和HashMap存在两个典型的区别:
    1、Hashtable是一个线程安全的Map实现类,但HashMap是线程不安全的实现,所以HashMap比Hashtable的性能高一点;但如果出现多个线程同时访问一个Map对象时,使用Hashtable类会更好。
    2、Hashtable不允许使用null作为key和value,如果试图把null放入Hashtable中,将会引发NullPointerException异常;但HashMap允许使用null作为key或value。

    3.1 null值作为HashMap的key、value情形

    由于HashMap里key不能重复,所以HashMap最多只有一个key-value对的key为null,但可以有多个key-value对的value为null。下面示范null值作为HashMap的key和value情形。

    import java.util.HashMap;
    public class NullInHashMap 
    {
    	public static void main(String[] args) 
    	{
    		var hm=new HashMap();
    		//试图将两个key-value对放入HashMap中
    		hm.put(null,null);
    		hm.put(null,null);//此处无法将key-value对放入,因为Map中已经有一个key-value对为null
    		//将一个value为null的值key-value放入到HashMap中
    		hm.put("a",null);
    		System.out.println(hm);//{null=null, a=null}
    	}
    }
    

    3.2 HashMap和Hashtable存储对象必须实现hashCode()和equals()方法

    HashMap和Hashtable不能保证key-value对的顺序。HashMap和Hashtable判断两个对象相等标准:两个key通过equals()方法返回true,两个key的HashCode值也要相同。
    HashMap和Hashtable独有的方法:
    containsValue():用于判断是否包含指定的value。HashMap和Hashtable判断两个value相等的标准:只要两个对象通过equals()方法比较返回true。

    import java.util.Hashtable;
    class A
    {
    	int count;
    	public A(int count)
    	{
    		this.count=count;
    	}
    	//根据count判断两个对象是否相等
    	@Override
    	public boolean equals(Object obj)
    	{
    		if(this==obj)
    			return true;
    		else if(obj!=null&&obj.getClass()==A.class)
    		{
    			var r=(A)obj;
    			return r.count==this.count;
    		}
    		else
    			return true;
    	}
    	//根据count值计算hashCode值
    	@Override
    	public int hashCode()
    	{
    		return this.count;
    	}
    }
    class B
    {
    	//重写equals()方法,B对象与任何对象通过该方法比较都返回true
    	@Override
    	public boolean equals(Object obj)
    	{
    		return true;
    	}
    }
    public class HashtableTest
    {
    	public static void main(String[] args)
    	{
    		var ht=new Hashtable();
    		ht.put(new A(60000),"疯狂Java讲义");
    		ht.put(new A(87563),"轻量级Java EE企业应用实战");
    		ht.put(new A(1232),new B());
    		System.out.println(ht);//{A@ea60=疯狂Java讲义, A@1560b=轻量级Java EE企业应用实战, A@4d0=B@a09ee92}
    
    		//只要两个对象通过equals()方法比较返回true
    		//Hashtable就认为它们有相等的value
    		//由于Hashtable中有一个B对象,它与任何对象通过equals()方法比较都返回true
    		System.out.println(ht.containsValue("测试字符集"));//true
    
    		//只要两个A对象的count相等,它们通过equals()方法比较返回true,且hashCode值相等
    		//Hashtable即认为它们有相同的key
    		System.out.println(ht.containsKey(new A(87563)));//true
    
    		//B对象为重写hashCode()方法,因此两个B对象尽管通过equals()比较返回true,但HashCode值
    		//不相等,Java为将它们认为是不同的key值。
    		System.out.println(ht.containsKey(new B()));//false
    
    		//删除元素
    		System.out.println(ht.remove(new A(1232)));//B@a09ee92
    		System.out.println(ht.remove(new A(1222)));//null
    		System.out.println(ht);//{A@ea60=疯狂Java讲义, A@1560b=轻量级Java EE企业应用实战}
    	}
    }
    

    当使用自定义类作为HashMap和Hashtable的key时,在重写该类的equals()和hashCode()方法时,要保证两个方法的判断结果一致——当两个key通过equals()方法返回true时,两个key通过hashCode()返回的HashCode值也应该相同。

    3.3 尽量不要修改可变对象作为HashMap和Hashtable的key

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

    import java.util.HashMap;
    public class HashMapErrorTest
    {
    	public static void main(String[] args)
    	{
    		var hm=new HashMap();
    		//此处的A类与上一个程序中A类是同一个类
    		hm.put(new A(60000),"疯狂Java讲义");
    		hm.put(new A(87563),"轻量级Java EE企业应用开发实战");
    		System.out.println(hm);//{A@ea60=疯狂Java讲义, A@1560b=轻量级Java EE企业应用开发实战}
    		
    		//获取HashMap的key,value的Iterator迭代器
    		//hm.keySet()返回key值所组成的HashSet集合
    		var it=hm.keySet().iterator();
    		//修改第一个元素的key
    		var first=(A)it.next();
    		first.count=87563;
    		System.out.println(hm);//{A@1560b=疯狂Java讲义, A@1560b=轻量级Java EE企业应用开发实战}
    
    		//只能删除没有被修改过的key所对应的kkey-value对
    		hm.remove(new A(87563));
    		System.out.println(hm);//{A@1560b=疯狂Java讲义}
    
    		//无法获取剩下的value
    		System.out.println(hm.get(new A(60000)));//null
    		System.out.println(hm.get(new A(87563)));//null
    	}
    }
    

    尽量不要使用可变对象作为HashMap、Hashtable的key,如果使用了,不要去修改作为key的可变对象。

    四、LinkedHashMap实现类

    LinkedHashMap是HashMap的子类,使用双向链表来维护key-value对的顺序(其实只需要考虑key的顺序),该链表维护Map的迭代顺序,迭代顺序与key-value对的插入顺序保持一致。
    LinkedHashMap可以避免对HashMap、Hashtable里key-value排序,同时也避免了使用TreeMap增加的成本。
    LinkedHashMap需要维护元素的插入顺序,因此性能略低于HashMap的性能;但因为它以链表来维护内部顺序,所以在迭代Map里的元素时将具有较好的性能。

    import java.util.LinkedHashMap;
    public class LinkedHashMapTest 
    {
    	public static void main(String[] args) 
    	{
    		var scores=new LinkedHashMap();
    		scores.put("语文",89);
    		scores.put("数学",98);
    		scores.put("英语",92);
    		//调用Map方法中的forEach()遍历Map
    		scores.forEach((key,value)->System.out.println(key+"-->"+value));
    	}
    }
    ---------- 运行Java捕获输出窗 ----------
    语文-->89
    数学-->98
    英语-->92
    
    输出完成 (耗时 0 秒) - 正常终止
    

    从上面运行的结果可以看出LinkedMap可以记住key-value对的添加顺序。

    五、使用Properties读写属性文件

    Properties类是Hashtable类的子类,该对象在处理属性文件时,十分方便(Windows操作平台上的ini文件就是一种属性文件)。Propertites类可以把Map对象和属性文件关联起来,从而可以把Map对象中的key-value对写入属性文件中,也可以把属性文件中的“属性名=属性值”加载到Map对象中。由于属性文件里的属性名、属性值都是字符串类型,所以Properties里的key、value都是字符串类型。
    Properties相当于一个key、value都是String类型的Map
    该类提供三个方法来修改Properties里的key、value值
    ★String getProperty(String key):获取Properties中指定属性名对应的属性值,类似于Map中的get(Object key)。
    ★String getProperty(String key,String defaultValue):与上一个方法类似,只是Properties中不存在指定的key时,则该方法指定默认值。
    ★Object setProperty(String key,String value):设置属性值,类似于HashMap中的put()方法。
    两个读写属性文件的方法
    ★void load(InputStream inStream):从属性文件(以输入流表示)中加载key-value对,把加载到的key-value对追加到Properties里(Properties是Hashtable的子类,它不能保证key-value对的顺序)。
    ★void store(OutputStream out,String comments):将Properties中的key-value对输出到指定的属性文件(以输出流表示)中。

    六、SortedMap接口和TreeMap实现类

    TreeMap就是一个红黑树数据结构,每个key-value对即作为红黑树的一个节点。TreeMap存储key-value对(节点)时,需要根据key对节点排序。TreeMap可以保证所有的key-value对处于有序状态
    TreeMap有两种排序方式:
    1、自然排序:
    TreeMap的所有key必须实现Comparable接口,而且接口中的所有key应该是同一个类的对象,否则将会抛出ClassCastException依次。
    2、定制排序:
    创建TreeMap时,传入一个Comparator对象,该对象负责对TreeMap中的所有key进行排序。采用定制排序时,可以不要求Map实现key的Comparable接口。
    TreeMap中判断两个key相等的标准:两个key通过compareTo()方法返回0,TreeMap即认为这两个key是相等的。
    使用自定义类作为TreeMap的key,且想让TreeMap良好的工作,则重写该类的equals()方法和compareTo()方法应当保持一致:两个key通过equals()方法返回true时,它们通过compareTo()返回0.
    TreeMap中提供了一系列的根据key顺序访问key-value对方法:

    方法 描述
    Map.Entry firstEntry() 返回该Map中最小的key所对应的key-value对,如果该Map为空,则返回null
    Object firstKey() 返回该Map中最小的key值,如果该Map为空,则返回null
    Map.Entry lastEntry() 返回该Map中最大的key所对应的key-value对,如果该Map为空,则返回null
    Object lastKey() 返回该Map中最大的key值,如果该Map为空,则返回null
    Map.Entry higherEntry(Object key) 返回该Map中位于key后一位的key-value对(即大于指定key的最小key所对应的key-value对)。如果该Map为空,则返回null
    Map.Entry higherKey(Object key) 返回该Map中位于key后一位的key值(即大于指定key的最小key)。如果该Map为空或不存在这样的key-value对,则返回null
    Map.Entry lowerEntry(Object key) 返回该Map中位于key前一位的key-value对(即小于指定key的最大key所对应的key-value对)。如果该Map为空,则返回null
    Map.Entry higherKey(Object key) 返回该Map中位于key前一位的key值(即小于指定key的最大key)。如果该Map为空或不存在这样的key-value对,则返回null
    NavigableMap subMap(Object fromKey,boolean fromInclusive,object toKey,boolean toInclusive) 返回该Map的子Map,其key范围从fromKey(是否包含取决于第二个参数)到toKey(是否包含取决于第二个参数)
    SortedMap subMap(Object fromKey,Object toKey) 返回该Map的子Map,其key的返回从fromKey到toKey(不包括)
    SortedMap tailMap(Object fromKey) 返回该Map的子Map,其key的范围是大于fromKey(包括)的所有key
    NavigableMap tailMap(Object fromKey,boolean inclusive) 返回该Map的子Map,其key的范围是大于fromKey(是否包括取决于第二个参数)的所有key
    SortedMap headMap(Object toKey) 返回该Map的子Map,其key的范围是小于toKey(不包括)的所有key
    NavigableMap headMap(Object toKey,boolean inclusive) 返回该Map的子Map,其key的范围是小于toKey(是否包括取决于第二个参数)的所有key

    6.1定制排序

    
    import java.util.*;
    class R implements Comparable
    {
    	int count;
    	public R(int count)
    	{
    		this.count = count;
    	}
    	public String toString()
    	{
    		return "R[count:" + count + "]";
    	}
    	// 根据count来判断两个对象是否相等。
    	public boolean equals(Object obj)
    	{
    		if (this == obj)
    			return true;
    		if (obj != null	&& obj.getClass() == R.class)
    		{
    			var r = (R) obj;
    			return r.count == this.count;
    		}
    		return false;
    	}
    	// 根据count属性值来判断两个对象的大小。
    	public int compareTo(Object obj)
    	{
    		var r = (R) obj;
    		return count > r.count ? 1 :
    			count < r.count ? -1 : 0;
    	}
    }
    public class TreeMapTest
    {
    	public static void main(String[] args)
    	{
    		var tm = new TreeMap();
    		tm.put(new R(3), "轻量级Java EE企业应用实战");
    		tm.put(new R(-5), "疯狂Java讲义");
    		tm.put(new R(9), "疯狂Android讲义");
    		System.out.println(tm);
    		// 返回该TreeMap的第一个Entry对象
    		System.out.println(tm.firstEntry());
    		// 返回该TreeMap的最后一个key值
    		System.out.println(tm.lastKey());
    		// 返回该TreeMap的比new R(2)大的最小key值。
    		System.out.println(tm.higherKey(new R(2)));
    		// 返回该TreeMap的比new R(2)小的最大的key-value对。
    		System.out.println(tm.lowerEntry(new R(2)));
    		// 返回该TreeMap的子TreeMap
    		System.out.println(tm.subMap(new R(-1), new R(4)));
    	}
    }
    ---------- 运行Java捕获输出窗 ----------
    {R[count:-5]=疯狂Java讲义, R[count:3]=轻量级Java EE企业应用实战, R[count:9]=疯狂Android讲义}
    R[count:-5]=疯狂Java讲义
    R[count:9]
    R[count:3]
    R[count:-5]=疯狂Java讲义
    {R[count:3]=轻量级Java EE企业应用实战}
    
    输出完成 (耗时 0 秒) - 正常终止
    

    6.2 定制排序

    import java.util.TreeMap;
    class R
    {
    	int count;
    	public R(int count)
    	{
    		this.count=count;
    	}
    	@Override 
    	public String toString()
    	{
    		return "R[count="+count+"]";
    	}
    }
    public class TreeMapSorted
    {
    	public static void main(String[] args)
    	{
    		//按照count越大排序越前排序
    		var tm=new TreeMap((o1,o2)->
    		{
    			var m1=(R)o1;
    			var m2=(R)o2;
    			return m1.count>m2.count?-1:m1.count<m2.count?1:0;
    		});
    		tm.put(new R(1),"one");
    		tm.put(new R(2),"two");
    		tm.put(new R(3),"three");
    		System.out.println(tm);
    
    	}
    }
    输出:{R[count=3]=three, R[count=2]=two, R[count=1]=one}
    

    七、WeeakHashMap类

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

    import java.util.WeakHashMap;
    public class WeakHashMapTest 
    {
    	public static void main(String[] args) 
    	{
    		var whm=new WeakHashMap();
    		//添加三个元素的key都是匿名字符串对象(没有其他引用)
    		whm.put(new String("语文"),new String("良好"));
    		whm.put(new String("数学"),new String("优秀"));
    		whm.put(new String("英语"),new String("中等"));
    
    		//向WeakHashMap中添加一个key-value对,该key是一个系统缓存的字符串对象
    		whm.put("java",new String("中等"));
    		System.out.println(whm);
    
    		//通知系统进行垃圾回收
    		System.gc();////告诉垃圾收集器打算进行垃圾收集,而垃圾收集器进不进行收集是不确定的
    		System.runFinalization();////强制调用已经失去引用的对象的finalize方法
    		//通常情况下只能看见一个key-value对
    		System.out.println(whm);
    	}
    }
    ---------- 运行Java捕获输出窗 ----------
    {java=中等, 数学=优秀, 英语=中等, 语文=良好}
    {java=中等}
    
    输出完成 (耗时 0 秒) - 正常终止
    

    如果需要使用WeakHashMap的key来保存对象的弱引用,则不要让该key所引用的对象具有任何强引用,否则将失去使用WeakHashMap的意义。

    八、IdentityHashMap类

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

    import java.util.*;
    public class IdentityHashMapTest
    {
    	public static void main(String[] args)
    	{
    		var ihm = new IdentityHashMap();
    		// 下面两行代码将会向IdentityHashMap对象中添加两个key-value对
    		ihm.put(new String("语文"), 89);
    		ihm.put(new String("语文"), 78);
    		// 下面两行代码只会向IdentityHashMap对象中添加一个key-value对
    		ihm.put("java", 93);
    		ihm.put("java", 98);
    		System.out.println(ihm);
    	}
    }
    ---------- 运行Java捕获输出窗 ----------
    {语文=78, java=98, 语文=89}
    
    输出完成 (耗时 0 秒) - 正常终止
    

    九、EnumMap实现类

    EnumMap实现类是一个与枚举类一起使用的Map实现,EnumMap中的所有key都必须是单个枚举类的枚举值。创建EnumMap时必须显式或隐式指定它所对应的枚举类。EnumMap具有以下特征:
    ★EnumMap在内部以数组形式保存,所以这种实现是非常搞笑的。
    ★EnumMap根据key的自然顺序(即枚举值在枚举类中的定义顺序)来维护key-value对的顺序。当程序通过keySet()、entrySet()、values()等方法遍历EnumMap时可以看到这种顺序结构。
    ★EnumMap不允许使用null作为key,但允许使用null作为value。

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

    十、各Map实现类的性能分析

    就常用实现类而言,HashMap和Hashtable实现机制几乎一样,但由于Hashtable是一个古老的、线程安全的集合,因此HashMap通常比Hashtable要快。
    TreeMap比HashMap和Hashtable要慢(尤其是插入、删除时),因为TreeMap使用红黑树管理key-value对(红黑树的每一个节点都是一个key-value对)
    对于一般场景,程序应多考虑适用HashMap,因为HashMap正是为快速查询设计的(HashMap底层使用数组来存储key-value对)。但如果程序需要一个总是排序好的Map时,则可以考虑使用TreeMap.
    LinkedHashMap比HashMap慢一点,因为它使用链表来保护Map中的key-value顺序。IdentityHashMap与HashMap基本相似的实现,只是它判断元素相等的条件需要两个key的identityHashCode()返回值相等。EnumMap性能最好,但它只能使用同一个枚举类的枚举值作为key。

  • 相关阅读:
    Oracle DBLink 使用情况
    asp.net里AjaxPro简单入门教程
    AjaxPro异步调用的超时设置
    ORA-01552: 非系统表空间 'USERS' 不能使用系统回退段的处理
    andoid 监听返回键退出
    WinForm调用user32.dll实现全屏
    C# Panel 打开 Form 窗口的方法
    Android获取日期及星期的方法
    WinForm 自定义对话框 获取返回值
    52. (待补) 实现对 无头单链表 的基本操作
  • 原文地址:https://www.cnblogs.com/weststar/p/12580530.html
Copyright © 2011-2022 走看看