hashmap 源码阅读

来源:互联网 发布:外国网购用什么软件 编辑:程序博客网 时间:2024/06/05 07:46

字段

Java代码  收藏代码
  1. static final int DEFAULT_INITIAL_CAPACITY = 16;//默认的数组长度  
  2.   
  3.   
  4. static final int MAXIMUM_CAPACITY = 1 << 30;//最大的数组长度  
  5.   
  6.   
  7. static final float DEFAULT_LOAD_FACTOR = 0.75f;//默认负载比率,数组不会全部填满数据   
  8.   
  9.   
  10. transient Entry[] table;//hashmap内部也是用数组来存储数据  
  11.   
  12.   
  13. transient int size;//实际存的数据的个数  
  14.   
  15.   
  16. int threshold;//实际当前可以存的数据量 = loadFactor*table.length  
  17.   
  18.   
  19. final float loadFactor; //实际的负载比率  

 

构造方法

Java代码  收藏代码
  1. public HashMap(int initialCapacity, float loadFactor) {  
  2.     if (initialCapacity < 0)  
  3.         throw new IllegalArgumentException("Illegal initial capacity: " +  
  4.                                            initialCapacity);  
  5.     if (initialCapacity > MAXIMUM_CAPACITY)  
  6.         initialCapacity = MAXIMUM_CAPACITY;  
  7.     if (loadFactor <= 0 || Float.isNaN(loadFactor))  
  8.         throw new IllegalArgumentException("Illegal load factor: " +  
  9.                                            loadFactor);  
  10.   
  11.     // Find a power of 2 >= initialCapacity  
  12.     int capacity = 1;  
  13.     while (capacity < initialCapacity)  
  14.         capacity <<= 1;    //不管你希望的初始长度是多少,系统都是对1做移位运算,最后的结果只能是2的次方  
  15.   
  16.     this.loadFactor = loadFactor;  
  17.     threshold = (int)(capacity * loadFactor);  //计算实际容量  
  18.     table = new Entry[capacity];  // 初始数组  
  19.     init();    //留给子类实现的一些操作,默认空  
  20. }  

 

put方法

 

Java代码  收藏代码
  1. public V put(K key, V value) {  
  2.     if (key == null)  
  3.         return putForNullKey(value);  
  4.     int hash = hash(key.hashCode());  
  5.     int i = indexFor(hash, table.length);  
  6.     for (Entry<K,V> e = table[i]; e != null; e = e.next) {  
  7.         Object k;  
  8.         if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {  
  9.             V oldValue = e.value;  
  10.             e.value = value;  
  11.             e.recordAccess(this);  
  12.             return oldValue;  
  13.         }  
  14.     }  
  15.   
  16.     modCount++;  
  17.     addEntry(hash, key, value, i);  
  18.     return null;  
  19. }  

 // 首先根据内部的一个hash算法计算出key对应的hash值(防止key本身的hashcode实现很差),再根据这个hash值和表的长度算出一个数组的index值,  然后在数组的该index值上看有没有存东西,东西一不一致(equals),不一致查找entry链表下一个(关于链表下面有说明),直到找到一个null, 就可以确定没有了,然后准备插入

 

Java代码  收藏代码
  1. void addEntry(int hash, K key, V value, int bucketIndex) {  
  2. ry<K,V> e = table[bucketIndex];  
  3.     table[bucketIndex] = new Entry<K,V>(hash, key, value, e);  
  4.     if (size++ >= threshold)  
  5.         resize(2 * table.length);  
  6. }  

 entry

Java代码  收藏代码
  1. static class Entry<K,V> implements Map.Entry<K,V> {  
  2.     final K key;  
  3.     V value;  
  4.     Entry<K,V> next;  
  5.     final int hash;  
  6.   
  7.     /** 
  8.      * Creates new entry. 
  9.      */  
  10.     Entry(int h, K k, V v, Entry<K,V> n) {  
  11.         value = v;  
  12.         next = n;  
  13.         key = k;  
  14.         hash = h;  
  15.     }  

 entry实际上是在实现单向链表结构,每一个entry内部有一个next指向他的后面,所以hashmap的内部数组每个格子存放的是实际上是一个链表的火车头,在最开始的时候table[index]位置上肯定是null,这个就是火车尾了。所以get的时候查找操作是以null为循环终点判断依据的。

在插入的时候把新加的元素替代原来的成为链表火车头,把之前元素的放在新加的火车头后面,

这么做是因为不同的key是有可能算出相同的hash值的,这种情况的时候,他们都会存放在数组的同一个index位置上,相同的hash不同的key的值彼此用链表连接起来,

 

get

Java代码  收藏代码
  1. public V get(Object key) {  
  2.     if (key == null)  
  3.         return getForNullKey();  
  4.     int hash = hash(key.hashCode());  
  5.     for (Entry<K,V> e = table[indexFor(hash, table.length)];  
  6.          e != null;  
  7.          e = e.next) {  
  8.         Object k;  
  9.         if (e.hash == hash && ((k = e.key) == key || key.equals(k)))  
  10.             return e.value;  
  11.     }  
  12.     return null;  
  13. }  

 get的时候就是算出key的hash值,算出数组的index位置,然后到数组的index位置上去遍历链表找到相应的key,如果hash重复的少的话,链表的长度就会很短,hashmap查找的速度就很快了,即使有重复,接下来就是遍历链表,所以要依靠好的算法保证hash重复的概率小,缩短链表的长度

http://www.iteye.com/topic/992907