Java 7源码分析第10篇 - Map集合的实现(一)

2014-11-24 07:14:30 · 作者: · 浏览: 3

可能我们经常在实际应用或面试中注重的是各个集合的特点,比如,Set集合不能存储重复元素,不能保持插入和大小顺序,Map集合存储key-value对等等。但是,如果你想学好Java,或者目标更高一点,想要精通Java,那仅掌握这些是远远不够的。其实集合中还有需多的知道需要我们去掌握和了解。

我们首先来看Map集合的实现而不是Set集合,因为Map和Set集合实现上非常的相似,Set中许多方法都是通过调用Map中的方法来实现功能的。为什么呢?因为Map可以说就是一个Set,Set集合不重复且无序、Map中的key也有这个特性。当我们将Map中的value看为key的附属时,获取到的所有key就可以组成一个Set集合。

来看一下两者实现的类图:

\

Set 集合框架图

\

Map集合框架图

先来看一下Map接口,源代码如下:

public interface Map
  
    {
    // Query Operations
    int size();
    boolean isEmpty();

    boolean containsKey(Object key);
    boolean containsValue(Object value);

    V get(Object key);

    // Modification Operations
    V put(K key, V value);
    V remove(Object key);

    // Bulk Operations
    /*
       The behavior of this operation is undefined if the
       specified map is modified while the operation is in progress.
     */
    void putAll(Map
    m);
    void clear();


    // Views
    Set
   
     keySet();// 由于Map集合的key不能重复,key之间无顺序,所以Map集合中的所有key就可以组成一个Set集合 Collection
    
      values(); Set
     
      > entrySet(); interface Entry
      
        { K getKey(); V getValue(); V setValue(V value); boolean equals(Object o); int hashCode(); } // Comparison and hashing boolean equals(Object o); int hashCode(); }
      
     
    
   
  

接口中有一个values方法,通过调用这个方法就可以返回Map集合中所有的value值;有一个keySet()方法,调用后可以得到所有Map中的 key值;调用entrySet()方法得到所有的Map中key-value对,以Set集合的形式存储。为了能够更好的表示这个key-value值,接口中还定义了一个Entry 接口,并且在这个接口中定义了一些操作key和value的方法。

public class HashMap
  
     extends AbstractMap
   
     implements Map
    
     , Cloneable, Serializable { // The default initial capacity - MUST be a power of two. static final int DEFAULT_INITIAL_CAPACITY = 16; static final int MAXIMUM_CAPACITY = 1 << 30; static final float DEFAULT_LOAD_FACTOR = 0.75f;//指定负载因子 // The table, resized as necessary. Length MUST Always be a power of two. // 使用Entry数组来存储Key-Value,类似于ArrayList用Object[]来存储集合元素 transient Entry[] table; transient int size; // The next size value at which to resize (capacity * load factor). // HashMap所能容的mapping的极限 int threshold; /* * 负载因子: */ final float loadFactor; transient int modCount; }
    
   
  
如上定义了一些重要的变量,其中的loadFactor是负载因子,增大值时可以减少Hash表(也就是Entry数组)所占用的内存空间,但会增加查询数据时时间的开销,而查询是最频繁的操作,减小值时会提高数据查询的性能,但是会增大Hash表所占用的内存空间,所以一般是默认的0.75。
threshold表示HashMap所能容纳的key-value对极限,如果存储的size数大于了threshold,则需要扩容了。
hashmap提供了几个构造函数,如下:
// 健HashMap的实际容量肯定大于等于initialCapacity,当这个值恰好为2的n次方时正好等于
    public HashMap(int initialCapacity, float loadFactor) {
        if (initialCapacity < 0)
            throw new IllegalArgumentException("Illegal initial capacity: " + initialCapacity);
        if (initialCapacity > MAXIMUM_CAPACITY)
            initialCapacity = MAXIMUM_CAPACITY;
        if (loadFactor <= 0 || Float.isNaN(loadFactor))
            throw new IllegalArgumentException("Illegal load factor: " + loadFactor);
        // Find a power of 2 >= initialCapacity
        int capacity = 1;
        while (capacity < initialCapacity)  // 计算出大于initialCapacity的最小的2的n次方值
            capacity <<= 1;
        this.loadFactor = loadFactor;
        threshold = (int)(capacity * loadFactor);//设置容量极限
        table = new Entry[capacity];
        init();
    }

    public HashMap(int initialCapacity) {
        this(initialCapacity, DEFAULT_LOAD_FACTOR);
    }

    public HashMap() {
        this.loadFactor = DEFAULT_LOAD_FACTOR;
        threshold = (int)(DEFAULT_INITIAL_CAPACITY * DEFAULT_LOAD_FACTOR);
        table = new Entry[DEFAULT_INITIAL_CAPACITY];
        init();
    }

    public HashMap(Map
   m) {
        this(Math.max((int) (m.size() / DEFAULT_LOAD_FACTOR) + 1,DEFAULT_INITIAL_CAPACITY), DEFAULT_LOAD_FACTOR);
        putAllForCreate(m);
    }
由第一个构造函数可以看出,其实际的capacity一般是大于我们指定的initialCapacity,除非initialCapacity正好是2的n次方值。接着就来说一下HashMap的实现原理吧。在这个类中开始处理定义了一个transient Entry[] 数组,这个Entry的实现如下:
private static class Entry
  
    implements Map.Entry
   
     { int hash; K key; V value; Entry
    
      next; protected Entry(int hash, K key, V value, Entry
     
       next) { this.hash = hash; this.key = key; this.value = value; this.next = next; } protected Object clone() { return new Entry<>(hash, key, value,(next==null   null : (Entry
      
       ) next.clone())); } // Map.Entry Ops public K getKey() { return key; } public V getValue() { return value; } public V setValue(V value) { if (value == null) throw new NullPointerException(); V oldValue = this.value; this.value = value; return oldValue; } public boolean equals(Object o) { if (!(o instanceof Map.Entry)) return false; Map.Entry e = (Map.Entry)o; return (key==null   e.getKey()==null : key.equals(e.getKey())) && (value==null   e.getValue()==null : value.equals(e.getValue())); } public int hashCode() { return hash ^ (value==null   0 : value.hashCode()); } public String toString() { return key.toString()+"="+value.toString(); } } 
      
     
    
   
  

了解了key-value存储的基本结构后,就可以考虑如