首页 > 代码库 > java集合之间的关系及实现细节——Set与Map

java集合之间的关系及实现细节——Set与Map

    首先,先谈一下Set和Map的一些特性及体系结构:

  1.1 Set和Map

  Set代表一种无序的、元素不可重复的集合。Map则代表是一种key-value对组成的集合,Map集合类似于传统的关联数组。表面上看它们之间的关系的相似性很少,实则Map和Set之间有着莫大的关联,可以这样说Map集合实则是Set集合的扩展。

  至于为什么是Set是一种无序的、元素不可重复的集合;Map集合类似传统的关系数组。理解这个非常重要,这个我在后面会进行补充说明。

  1.2 Set和Map之间的关系

  先看看Set集合的继承体系,再来看Map集合的类继承体系: 

  从上图灰色阴影部分可以看出Map和Set的集合实现类除了后面Set和Map部分不一样,前面的类或接口的名称以及其继承结构完全一样。是偶然吗?答案绝对不是。而且你若是用过Map的话,你会发现Map不允许再现相同的key,且Map在遍历的时候,key是无序的,正是对应Map中keySet()方法。这与上述Set集合的特征又是惊人的相似。下面我会用一段小程序来说明Set为何与Map这么相似了。

  先说一下我的思路: 

  (1)在我的印象中:map的key与value存在的关系如下图:

  (2)既然Map中的key与value存在一一对应关系,那么,现在我们换一种思路来思考,把key-value如下图:

  由此可见,如上述图3.4表明的一样,如果将key-value绑定在一起,那么Map说白了就是个(key-value)的Set集合

  由上述思路,下面是程序实现由Set实现Map:

(1)包含key-value的entry实现代码:

 1 package com.ngu4k.test.collection; 2  3 import java.io.Serializable; 4 import java.util.Map.Entry; 5  6 public class SimpleEntry<K,V> implements Entry<K,V>,Serializable{ 7     private static final long serialVersionUID = -4352059623055803486L; 8     private final K key; 9     private V value;10     //定义两个如下构造器11     public SimpleEntry(K key,V value)12     {13         this.key = key;14         this.value =http://www.mamicode.com/ value;15     }16     17     public SimpleEntry(Entry<? extends K,? extends V> entry)18     {19         this.key = entry.getKey();20         this.value =http://www.mamicode.com/ entry.getValue();21     }22     //获取key23     public K getKey() 24     {25         return key;26     }27     //获取value28     public V getValue() 29     {30         return value;31     }32     //改变该key-value对的value值33     public V setValue(V value) 34     {35         V oldValue = http://www.mamicode.com/this.value;36         this.value =http://www.mamicode.com/ value;37         return oldValue;38     }39     //根据key比较两个对象是否相等40     public boolean equals(Object o)41     {42         if(o == this){43             return true;44         }45         if(o.getClass() == SimpleEntry.class){46             SimpleEntry se = (SimpleEntry)o;47             return se.getKey().equals(getKey());48         }49         return false;50     }51     //根据key获得hashCode的值52     public int hashCode(){53         return key == null ? 0 : key.hashCode();54     }55     56     public String toString(){57         return key + "=" + value;58     }59 }

(2)由Set到Map的简单实现代码:

  1 package com.ngu4k.test.collection.map;  2   3 import java.util.HashSet;  4 import java.util.Iterator;  5 import java.util.Map;  6   7 import com.ngu4k.test.collection.SimpleEntry;  8   9 /** 10  * Set到Map的简单实现 11  * @author ikouer 12  * @param <K> 13  * @param <V> 14  */ 15 public class Set2Map<K,V> extends HashSet<SimpleEntry<K,V>>{ 16  17     private static final long serialVersionUID = 1596710712757978024L; 18      19     //清空Set2Map中key-value对 20     public void clear() 21     { 22         super.clear(); 23     } 24     //查看是否包含指定的key 25     public boolean containsKey(K key) 26     { 27         return super.contains(new SimpleEntry<K, V>(key,null)); 28     } 29     //查看是否包含指定的value 30     public boolean containsValue(V value) 31     { 32         for(SimpleEntry<K,V> se : this) 33         { 34             if(se.getValue().equals(value)) 35             { 36                 return true; 37             } 38         } 39         return false; 40     } 41     //根据指定的key获取对应的value 42     public V get(K key) 43     { 44         for(SimpleEntry<K,V> se : this) 45         { 46             if(se.getKey().equals(key)){ 47                 return se.getValue(); 48             } 49         } 50         return null; 51     } 52     //把指定的key-value添加到集合中 53     public V put(K key,V value) 54     { 55         this.add(new SimpleEntry<K,V>(key,value)); 56         return value; 57     } 58     //把一个Map集合添加到Set2Map集合中 59     public void putAll(Map<? extends K,? extends V> om) 60     { 61         for(K key : om.keySet()) 62         { 63             this.add(new SimpleEntry<K,V>(key,om.get(key))); 64         } 65     } 66     //根据指定的key移除对应的key-value 67     public V remove(K key) 68     { 69         for(Iterator<SimpleEntry<K,V>> it = this.iterator(); it.hasNext();){ 70             SimpleEntry<K,V> se = it.next(); 71             if(se.getKey().equals(key)) 72             { 73                 V rv = se.getValue(); 74                 it.remove(); 75                 return rv; 76             } 77         } 78         return null; 79     } 80     //获取集合大小 81     public int size(){ 82         return super.size(); 83     } 84      85     public static void main(String[] args) { 86         Set2Map<String,Integer> smap = new Set2Map<String,Integer>(); 87         //将key-value放入集合 88         smap.put("语文", 88); 89         smap.put("数学", 99); 90         smap.put("英语", 102); 91         System.out.println(smap); 92         System.out.println(smap.size()); 93         //执行移除对称key的entry 94         smap.remove("数学"); 95         System.out.println("删除key为\"数学\"的Entry之后:"+smap); 96         //获取key为语文的value值 97         System.out.println("key为\"语文\"的value值:"+smap.get("语文")); 98         //判断是否包含key 99         System.out.println("是否包含key为\"英语\"的entry:"+smap.containsKey("英语"));100         //判断是否包含value101         System.out.println("是否包含value为102:"+smap.containsValue(102));102         //执行clear方法103         smap.clear();104         System.out.println("执行完clear()方法之后的集合:"+smap);105         106     }107 }

 

此处的提供的Set2Map集合的功能甚至可以媲美Map集合的功能
而TreeSet底层也是采用TreeMap实现的。至于Tree(二叉树)是怎么实现,读者可参考疯狂java系列书籍或到网上自行搜索。
至此,我想大家应该知道Set中的元素为什么是无序且不可重复的了吧。(因为Set集合的底层全部采用Map集合来实现的)
下面再和大家分析一下java中的Map的实现--HashMap的原理:
1.3 HashMap的实现原理
  要搞清hashmap是什么,首先要清楚它的数据结构,在java编程语言中,最基本的结构就是数组和模拟指针了,几乎所有的数据都可以用这两具基本的结构来构造的,hashmap也不例外。java中的hashmap实际上是一个数组和链表的结合体(在数据结构中,一般称之为“链表散列”)
  java中的hashmap是以key-value的模式存取的,实际上它也是存放在数组当中的。过程是这样的:首先会得到key的hashcode,然后通过hashcode(key)%size,来得到数组的索引,把entry放入数组中。请看下图(横排表示数组,纵排表示链表):
  
由上图可知,hashMap其实是就是一个数组。看一下hashMap的源代码:
上面table中的定义为:  transient Entry[] table;
由此可见hashmap在初始化时会创建一个Entry类型的数组。再看一下Entry的源代码:
  可以看到entry中持有一个指向下一个元素的引用,这样就构成了链表。
  当我们往hashmap中put元素中,首先根据key的hash值得到这个元素在数组中的位置(即下标),然后可这个元素放入到对应的位置就行了。如果这个位置已经有了其它元素,那么在同一个位置的元素将以链表的形式存储,新加入的元素放在链头,最先加入的元素放在链尾。从hashmap中get元素的时候,首先根据传入的key值的hash得到元素在数组中的存放位置,然后再通过key的equals方法来得到需要的元素。
  java中的Set与Map并不是我们想像中的那么神秘,其实很简单。
  本文参考:《疯狂java》、《java中的哈希表》
  上述是Map与Set的关系及一些实现细节。若有不到之处,敬请各位指导并谅解,谢谢!
  下篇文章我会把List与Map的关系及ArrayList,LinkList的实现细节做一些详细的记录。
      欢迎大家阅读与转载。转载请注入本文原址,谢谢!