java集合之间的关系及实现细节(1)——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 = value;
15     }
16     
17     public SimpleEntry(Entry<? extends K,? extends V> entry)
18     {
19         this.key = entry.getKey();
20         this.value = entry.getValue();
21     }
22     //获取key
23     public K getKey() 
24     {
25         return key;
26     }
27     //获取value
28     public V getValue() 
29     {
30         return value;
31     }
32     //改变该key-value对的value值
33     public V setValue(V value) 
34     {
35         V oldValue = this.value;
36         this.value = 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         //判断是否包含value
101         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的实现细节做一些详细的记录。
      欢迎大家阅读与转载。转载请注入本文原址,谢谢!
 
posted @ 2014-06-19 12:15  ikouer  阅读(886)  评论(0编辑  收藏  举报