Java中常用的Map实现类主要有:HashMap、HashTable、TreeMap、LinkedHashMap

一:HashMap

HashMap介绍

    HashMap的底层其实是“链表的数组”,即:每个元素其实存放着一个链表,链表存放着哈希值相同的对象们。HashMap是线程不安全的。

    Map集合学习

      1)HashMap不是简单的用key的hashcode()值作为元素的存放下标的,而是通过二次哈希——把key的hashcode()传进HashMap自定义的hash(h)方法中计算位置(有可能大于数组长度了,所以还要对数组长取余),然后通过indexFor(h,len)方法计算出具体的数组下标(用按位与取代取余加快效率),尽量让key尽可能均匀的分配到数组上去,避免造成Hash堆积(某一下标处存放的链表过长)。

         2)HashMap的冲突解决:如果有两个key的hashcode相同,那么经过hash(h)二次哈希后得到的数组索引是一样的,此时就要判断这两个key是否是同一对象:如果两个key的equals方法返回true,则说明两个key是同一对象,则此时把新值覆盖掉旧值;如果equals返回false,则说明是两个不同的key但分配到了同一数组索引位存放,则此时把新增的value添加到该索引位的链表尾。

    3)插入元素时,通过key的hashcode()以及hash算法决定索引,通过equals()决定是插入链表还是覆盖原有值;

              读取元素时,通过key的hashcode()以及hash算法找到索引,通过equals()遍历链表找到相对应的结点值;

            (注:Map存储的是 键值对,不是单指用 key 来索引 value。而是用key 来索引 Entry!Entry就是我们说的 键值对!因此,get()时确定槽位后,在遍历Entry链表时才可以把查找的key与链表结点的key进行比较。)

主要源码:

  1 //新建HashMap,其实是新建了一个数组
  2 public HashMap(int initialCapacity, float loadFactor) {
  3      // initialCapacity代表初始化HashMap的容量,它的最大容量是MAXIMUM_CAPACITY = 1 << 30。
  4         if (initialCapacity < 0)
  5             throw new IllegalArgumentException("Illegal initial capacity: " +
  6                                                initialCapacity);
  7         if (initialCapacity > MAXIMUM_CAPACITY)
  8             initialCapacity = MAXIMUM_CAPACITY;
  9 
 10      // loadFactor代表它的负载因子,默认是是DEFAULT_LOAD_FACTOR=0.75,用来计算threshold临界值的。
 11         if (loadFactor <= 0 || Float.isNaN(loadFactor))
 12             throw new IllegalArgumentException("Illegal load factor: " +
 13                                                loadFactor);
 14 
 15         // Find a power of 2 >= initialCapacity
 16         int capacity = 1;
 17         while (capacity < initialCapacity)
 18             capacity <<= 1;
 19 
 20         this.loadFactor = loadFactor;
 21         threshold = (int)(capacity * loadFactor);
 22         table = new Entry[capacity];//创建数组
 23         init();
 24     }
 25 
 26 //插入元素
 27 public V put(K key, V value) {
 28     // HashMap允许存放null键和null值。
 29     // 当key为null时,调用putForNullKey方法,将value放置在数组第一个位置。
 30     if (key == null)
 31         return putForNullKey(value);
 32     // 根据key的hashCode重新计算hash值。
 33     int hash = hash(key.hashCode());
 34     // 搜索指定hash值所对应table中的索引。
 35     int i = indexFor(hash, table.length);
 36     // 如果 i 索引处的 Entry 不为 null,通过循环不断遍历 e 元素的下一个元素。
 37     for (Entry<K,V> e = table[i]; e != null; e = e.next) {
 38         Object k;
 39         if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
 40             V oldValue = e.value;
 41             e.value = value;
 42             e.recordAccess(this);
 43             return oldValue;
 44         }
 45     }
 46     // 如果i索引处的Entry为null,表明此处还没有Entry。
 47     // modCount记录HashMap中修改结构的次数
 48     modCount++;
 49     // 将key、value添加到i索引处。
 50     addEntry(hash, key, value, i);
 51     return null;
 52 }
 53 void addEntry(int hash, K key, V value, int bucketIndex) {
 54     // 获取指定 bucketIndex 索引处的 Entry 
 55     Entry<K,V> e = table[bucketIndex];
 56     // 将新创建的 Entry 放入 bucketIndex 索引处,并让新的 Entry 指向原来的 Entry
 57     table[bucketIndex] = new Entry<K,V>(hash, key, value, e);
 58     // 如果 Map 中的 key-value 对的数量超过了极限
 59     if (size++ >= threshold)
 60     // 把 table 对象的长度扩充到原来的2倍。
 61         resize(2 * table.length);
 62 }
 63 
 64 static int hash(int h) {
 65     h ^= (h >>> 20) ^ (h >>> 12);
 66     return h ^ (h >>> 7) ^ (h >>> 4);
 67 }
 68 
 69 static int indexFor(int h, int length) {
 70     return h & (length-1);
 71 }
 72 //它通过 h & (table.length -1) 来得到该对象的保存位,而HashMap底层数组的长度总是 2 的n 次方,这是HashMap在速度上的优化。
 73 //当length总是 2 的n次方时,h& (length-1)运算等价于对length取模,也就是h%length,但是&比%具有更高的效率。
 74 
 75 //读取元素
 76 public V get(Object key) {
 77     if (key == null)
 78         return getForNullKey();
 79     int hash = hash(key.hashCode());
 80     for (Entry<K,V> e = table[indexFor(hash, table.length)];
 81         e != null;
 82         e = e.next) {
 83         Object k;
 84         if (e.hash == hash && ((k = e.key) == key || key.equals(k)))
 85             return e.value;
 86     }
 87     return null;
 88 }
 89 
 90 //HashMap数组扩容
 91           void resize(int newCapacity) {
 92                 Entry[] oldTable = table;
 93                 int oldCapacity = oldTable.length;
 94                 //如果当前的数组长度已经达到最大值,则不在进行调整
 95                 if (oldCapacity == MAXIMUM_CAPACITY) {
 96                     threshold = Integer.MAX_VALUE;
 97                     return;
 98                 }
 99                 //根据传入参数的长度定义新的数组
100                 Entry[] newTable = new Entry[newCapacity];
101                 //按照新的规则,将旧数组中的元素转移到新数组中
102                 transfer(newTable);
103                 table = newTable;
104                 //更新临界值
105                 threshold = (int)(newCapacity * loadFactor);
106             }
107 
108           //旧数组中元素往新数组中迁移
109             void transfer(Entry[] newTable) {
110                 //旧数组
111                 Entry[] src = table;
112                 //新数组长度
113                 int newCapacity = newTable.length;
114                 //遍历旧数组
115                 for (int j = 0; j < src.length; j++) {
116                     Entry<K,V> e = src[j];
117                     if (e != null) {
118                         src[j] = null;
119                         do {
120                             Entry<K,V> next = e.next;
121                             int i = indexFor(e.hash, newCapacity);
122                             e.next = newTable[i];
123                             newTable[i] = e;
124                             e = next;
125                         } while (e != null);
126                     }
127                 }
128             }
View Code

相关文章:

  • 2022-01-07
  • 2021-10-05
  • 2021-12-30
  • 2022-12-23
  • 2021-11-15
  • 2021-12-22
  • 2021-05-21
  • 2021-11-04
猜你喜欢
  • 2021-05-17
  • 2021-06-08
  • 2022-01-17
  • 2022-12-23
  • 2021-08-15
  • 2021-10-12
  • 2021-12-27
相关资源
相似解决方案