zoukankan      html  css  js  c++  java
  • HashMap源码简单分析

      1 还是老习惯,一边看,一边添加注释,希望坚持下去,HashMap的基本源码进行了分析,内部一些接口和设计还没来得及看
      2 
      3 一、成员
      4 
      5 1、transient Entry[] table;
      6 
      7 HashMap内部维护了一个内部类-Entry,用来存放键值对,这个Entry实现了Map.Entry这一Map的内部接口Entry,HashMap本质上来讲是由数组和Entry链表组成的数据结构
      8 
      9 2、 static final float DEFAULT_LOAD_FACTOR = 0.75f;
     10 
     11 加载因子,加载因子越大,hash表(即Entry数组)所占空间越少,但会影响查询性能(因为需要通过链表一个挨一个向下查询),加载因子越小,hash表(即Entry数组)所占空间越多,这时查询效率较高,但是hash表所占空间较多
     12 
     13 3、static final int DEFAULT_INITIAL_CAPACITY = 16;
     14 
     15 4、/**
     16 
     17   * The next size value at which to resize (capacity * load factor).
     18   * @serial
     19   */
     20   int threshold;
     21 
     22 5、static final int MAXIMUM_CAPACITY = 1 << 30;
     23 
     24 6、static final int DEFAULT_INITIAL_CAPACITY = 16;
     25 
     26 7、final float loadFactor
     27 
     28 决定什么时候进行扩容
     29 
     30 二、方法
     31 
     32 1、核心构造方法
     33 
     34 public HashMap(int initialCapacity, float loadFactor) {
     35 if (initialCapacity < 0)
     36 throw new IllegalArgumentException("Illegal initial capacity: " +
     37 initialCapacity);
     38 if (initialCapacity > MAXIMUM_CAPACITY)
     39 initialCapacity = MAXIMUM_CAPACITY;
     40 if (loadFactor <= 0 || Float.isNaN(loadFactor))
     41 throw new IllegalArgumentException("Illegal load factor: " +
     42 loadFactor);
     43 
     44 // Find a power of 2 >= initialCapacity
     45 int capacity = 1;
     46 while (capacity < initialCapacity)
     47 capacity <<= 1;
     48 
     49 this.loadFactor = loadFactor;
     50 threshold = (int)(capacity * loadFactor);
     51 table = new Entry[capacity];  //capacity代表数组的长度
     52 init();
     53 }
     54 
     55 2、在key对象的hashCodr()方法的基础上再做hash,避免一些不好的hashCode()方法
     56 
     57 //Null keys always map to hash 0,  如果key为null,那么hash()方法的到的hash值为0,再调用indexFor方法得到的数组的索引值也为0,所以key为null的Entry存在数组下标为0的位置
     58 
     59 static int hash(int h) {
     60 // This function ensures that hashCodes that differ only by
     61 // constant multiples at each bit position have a bounded
     62 // number of collisions (approximately 8 at default load factor).
     63 h ^= (h >>> 20) ^ (h >>> 12);
     64 return h ^ (h >>> 7) ^ (h >>> 4);
     65 }
     66 
     67 3、根据2中获得的hash值和数组的长度得到Entry对应的数组的索引
     68 
     69 static int indexFor(int h, int length) {
     70 return h & (length-1);   //屏蔽高位,保证与操作后最大值为length-1
     71 }
     72 
     73 4、根据key获取value
     74 
     75 public V get(Object key) {
     76 if (key == null)
     77 return getForNullKey();  //如果key为null则直接取index为0的Entry对应的value值
     78 int hash = hash(key.hashCode());  //生成Entryhash值
     79 for (Entry<K,V> e = table[indexFor(hash, table.length)];  //获取header
     80 e != null;
     81 e = e.next) {  //链表向下走
     82 Object k;
     83 if (e.hash == hash && ((k = e.key) == key || key.equals(k)))  //判断key是否相同
     84 return e.value;
     85 }
     86 return null;
     87 }
     88 
     89 5、获取key为null的key对应的值(注意:这里使用在链表中查找的方式,因为index为0的链表上不是只有key为null的Entry)
     90 
     91 private V getForNullKey() {
     92 for (Entry<K,V> e = table[0]; e != null; e = e.next) {
     93 if (e.key == null)
     94 return e.value;
     95 }
     96 return null;
     97 }
     98 
     99 6、添加键值对
    100 
    101 public V put(K key, V value) {
    102 
    103 /*如果key存在则对value进行修改并将原value返回*/
    104 if (key == null)
    105 return putForNullKey(value);
    106 int hash = hash(key.hashCode());
    107 int i = indexFor(hash, table.length);
    108 for (Entry<K,V> e = table[i]; e != null; e = e.next) {
    109 Object k;
    110 if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
    111 V oldValue = e.value;
    112 e.value = value;
    113 e.recordAccess(this);
    114 return oldValue;
    115 }
    116 }
    117 
    118 /*如果key不存在则新增键值对*/
    119 
    120 modCount++;
    121 addEntry(hash, key, value, i);
    122 return null;
    123 }
    124 
    125 7、新增加键值对
    126 
    127 void addEntry(int hash, K key, V value, int bucketIndex) {
    128 Entry<K,V> e = table[bucketIndex];  //获取header
    129 table[bucketIndex] = new Entry<K,V>(hash, key, value, e);  //new一个新Entry,并将后指针指向原header,新加的Entry成为新header
    130 if (size++ >= threshold)  //如果元素个数超过阈值,则进行扩容
    131 resize(2 * table.length);  //扩容为原来的2倍
    132 }
    133 
    134 8、扩容
    135 
    136 void resize(int newCapacity) {
    137 Entry[] oldTable = table;
    138 int oldCapacity = oldTable.length;
    139 if (oldCapacity == MAXIMUM_CAPACITY) {
    140 threshold = Integer.MAX_VALUE;
    141 return;
    142 }
    143 
    144 Entry[] newTable = new Entry[newCapacity];  //扩容为新capacity
    145 transfer(newTable);  //将所有的Entry迁移到新的数组中去
    146 table = newTable;
    147 threshold = (int)(newCapacity * loadFactor);  //重新计算阈值
    148 }
    149 
    150 9、将所有Entry迁移到新数组中
    151 
    152 void transfer(Entry[] newTable) {
    153 Entry[] src = table;
    154 int newCapacity = newTable.length;
    155 
    156 /*遍历Entry数组的0-(size-1)的索引对应的Entry链表,并将链表上的Entry重新计算在新数组中的索引并迁移到新数组的Entry链中*/
    157 for (int j = 0; j < src.length; j++) {
    158 Entry<K,V> e = src[j];
    159 if (e != null) {
    160 src[j] = null;  //for GC
    161 do {    //遍历处理某个索引上的Entry链
    162 Entry<K,V> next = e.next;
    163 int i = indexFor(e.hash, newCapacity);  //重新计算索引 
    164 
    165 /*将所有Entry分别放到应该放到的indexEntry链上*/
    166 e.next = newTable[i];
    167 newTable[i] = e;
    168 e = next;
    169 } while (e != null);
    170 }
    171 }
    172 }
  • 相关阅读:
    autocomplete自动完成搜索提示仿google提示效果
    实现子元素相对于父元素左右居中
    javascript 事件知识集锦
    让 IE9 以下的浏览器支持 Media Queries
    「2013124」Cadence ic5141 installation on CentOS 5.5 x86_64 (limited to personal use)
    「2013420」SciPy, Numerical Python, matplotlib, Enthought Canopy Express
    「2013324」ClipSync, Youdao Note, GNote
    「2013124」XDMCP Configuration for Remote Access to Linux Desktop
    「2013115」Pomodoro, Convert Multiple CD ISO to One DVD ISO HowTo.
    「2013123」CentOS 5.5 x86_64 Installation and Configuration (for Univ. Labs)
  • 原文地址:https://www.cnblogs.com/lige-H/p/7410944.html
Copyright © 2011-2022 走看看