集合--HashMap

HashMap
源码探究

存在包:
 package java.util;
继承关系:
 继承AbstractMap(JDK1.2之后提出),实现了Map接口,可以克隆,可以序列化。

public class HashMap<K,V>
    extends AbstractMap<K,V>
    implements Map<K,V>, Cloneable, Serializable

底层数据结构:
 哈希表(数组加链表),HashMap底层的实现是数组 + 链表实现,数组中存储的一个个entry。
基本属性:

//哈希表中数组默认初始值大小为16
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16 
//哈希表中数组最大容量值
static final int MAXIMUM_CAPACITY = 1 << 30;
//默认的加载因子,衡量HashMap满的程度  ——————》扩容使用
static final float DEFAULT_LOAD_FACTOR = 0.75f;
transient Entry[] table;

//HashMap中存储entry实体的个数
transient int size;
//扩容的阈值  ===》 capacity * loadFactor  容量 * 加载因子
int threshold;
//加载因子
final float loadFactor;
//版本号
transient volatile int modCount;


static final Entry<?,?>[] EMPTY_TABLE = {};
transient Entry<K,V>[] table = (Entry<K,V>[]) EMPTY_TABLE;
static class Entry<K,V>{
   final K key; //键值对的key
   V value;   //键值对的value
   Entry<K,V> next;  //next节点
   int hash;  //和key相关的hash
}
HashMap底层的实现是数组+链表实现:数组中存储的是一个个entry实体,hash到同一个索引位置的数据通过链表链接起来


static final int TREEIFY_THRESHOLD = 8;//扩容的阈值 = capacity * loadFactor
static final int UNTREEIFY_THRESHOLD = 6;
static final int MIN_TREEIFY_CAPACITY = 64;

构造函数:

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);
        //加载因子、扩容阈值初始化
        this.loadFactor = loadFactor;
        this.threshold = tableSizeFor(initialCapacity);
    }
    
//通过默认加载因子和默认容量初始化
 public HashMap(int initialCapacity) {
        this(initialCapacity, DEFAULT_LOAD_FACTOR);
    }
    
 public HashMap() {
        this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted
    }
    
//通过map集合来初始化当前集合
    public HashMap(Map<? extends K, ? extends V> m) {
        this(Math.max((int) (m.size() / DEFAULT_LOAD_FACTOR) + 1,
                      DEFAULT_INITIAL_CAPACITY), DEFAULT_LOAD_FACTOR);
        inflateTable(threshold);

        putAllForCreate(m);
    } 

增长方式:
  扩容时机:当前存储元素的个数size>=扩容阈值threshold时考虑扩容,扩容大小为2倍的数组大小table.length(数组要满足2的指数级关系)。

CRUD方法研究(增删改查):

put():添加元素:
思路:

1.第一次插入元素,对table数组进行初始化;

2.判断插入的数据Key是否为null:


 a.key为null,将key为null的数据存储在table[0]位置,
 遍历该位置下的链表,存在key为null的结点,
 将新传入的value更新,若该链表下不存在key为null的结点,
 创建新的结点entry插入到链表中,调用addEntry();
 
 b.key不为null,通过key来哈希到该数据所在的索引位置:
 key相关的哈希值,遍历该位置i下的链表,
 判断key是否相等,替换value,不等创建新entry;


3.判断当前存储元素的个数size>=扩容阈值threshold时进行扩容,
扩容大小为2倍的数组大小table.length(数组要满足2的指数级关系),
创建新的table数组,并且将原来集合上的元素全部进行hash,
找到新的对应位置进行插入,更新新插入元素的索引位置。

public V put(K key, V value) {
      //第一次插入元素,需要对table数组进行初始化:注意:数组大小为2的指数关系
      if (table == EMPTY_TABLE) {
          inflateTable(threshold);
      }
      //可以存储key为null的情况
      if (key == null)
          return putForNullKey(value);
      //key不为null 
      //通过key来哈希找到该数据所存在的索引位置:key相关的哈希值   
      int hash = hash(key);
      int i = indexFor(hash, table.length);
      //遍历该位置i下面的链表:(判断key是否存在,存在替换value,不存在创建新entry)
      for (Entry<K,V> e = table[i]; e != null; e = e.next) {
          Object k;
          //判断元素是否相等
          //e.hash值与通过key的hash是否相等	 key为null             key不为null
          if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
              V oldValue = e.value;
              e.value = value;
              e.recordAccess(this);
              return oldValue;
          }
      }
      modCount++;
      addEntry(hash, key, value, i);
      return null;
  }
  
  插入key为null情况
  key为null做特殊处理,存储在table索引位0号位置
  遍历该位置下的链表,查看key为null的节点是否存在,存在即将value更新为传入的value
  若该链表下不存在则创建新的entry节点插入该链表
  private V putForNullKey(V value) {
      for (Entry<K,V> e = table[0]; e != null; e = e.next) {
          if (e.key == null) {
              V oldValue = e.value;
              e.value = value;
              e.recordAccess(this);
              return oldValue;
          }
      }
      modCount++;
      addEntry(0, null, value, 0);
      return null;
  } 
  
  
  //该方法主要是将Object转化成一个整型
      final int hash(Object k) {
          int h = hashSeed;
          if (0 != h && k instanceof String) {
              return sun.misc.Hashing.stringHash32((String) k);
          }
  
          h ^= k.hashCode();//异或
  
          h ^= (h >>> 20) ^ (h >>> 12);
          return h ^ (h >>> 7) ^ (h >>> 4);
      }
  
  //主要将hash生成的整型转化为链表数组中的下标
      static int indexFor(int h, int length) {
          // assert Integer.bitCount(length) == 1 : "length must be a non-zero power of 2";
          return h & (length-1);//取模  & 运算符效率高
      }     
      
      length 16
      1001 1111 0110 0110
      0000 0000 0000 1111
      -------------------
      0000 0000 0000 0110
      ==>6
  
  void addEntry(int hash, K key, V value, int bucketIndex) {
      // newCapacity(table.length) *loadFactor
      if ((size >= threshold) && (null != table[bucketIndex])) {
          //扩容
          resize(2 * table.length);
          
          //更新新插入元素的索引位置
          hash = (null != key) ? hash(key) : 0;
          bucketIndex = indexFor(hash, table.length);
      }
  
      createEntry(hash, key, value, bucketIndex);
  }
  
 //扩容时机:当前存储元素的个数size>=扩容阈值threshold时考虑扩容
   
//扩容大小为2倍的数组大小table.length(数组要满足2的指数级关系)
  
  
  void createEntry(int hash, K key, V value, int bucketIndex) {
        Entry<K,V> e = table[bucketIndex];
        table[bucketIndex] = new Entry<>(hash, key, value, e);
        size++;
    }
//元素作为当前索引位置的头部元素进行插入
  Entry(int h, K k, V v, Entry<K,V> n) {
            value = v;
            next = n;
            key = k;
            hash = h;
        }
  void resize(int newCapacity) {
      Entry[] oldTable = table;
      int oldCapacity = oldTable.length;
      if (oldCapacity == MAXIMUM_CAPACITY) {
          threshold = Integer.MAX_VALUE;
          return;
      }
  
      Entry[] newTable = new Entry[newCapacity];
      transfer(newTable, initHashSeedAsNeeded(newCapacity));
      table = newTable;
      threshold = (int)Math.min(newCapacity * loadFactor, MAXIMUM_CAPACITY + 1);
  }
  
  //创建新的table数组,并且将原来集合上的元素全部进行hash,找到新的对应位置进行插入
 
  void transfer(Entry[] newTable, boolean rehash) {
      int newCapacity = newTable.length;
      for (Entry<K,V> e : table) {
          while(null != e) {
              Entry<K,V> next = e.next;
              if (rehash) {
                  e.hash = null == e.key ? 0 : hash(e.key);
              }
              int i = indexFor(e.hash, newCapacity);
              e.next = newTable[i];
              newTable[i] = e;
              e = next;
          }
      }
  }
  

get():获取元素:通过键值Key来查找Value;

1、判断key是否为null,是则特殊处理,知道到0号索引位置查找元素

2、先通过hash找到索引位置,通过索引位置找到当前链表,通过判断key是否相等找到value进行返回
public V get(Object key) {
      //判断key是否为null,是则特殊处理,直到0号索引位置查找元素
          if (key == null)
              return getForNullKey();
      //通过hash找到索引位置,通过索引位置找到当前链表,通过判断Key是否相等找到value值并返回
          int hash = hash(key.hashCode());
          for (Entry<K,V> e = table[indexFor(hash, table.length)];
               e != null;
               e = e.next) {
              Object k;
              if (e.hash == hash && ((k = e.key) == key || key.equals(k)))
                  return e.value;
          }
          return null;
      }
  
     
      private V getForNullKey() {
          for (Entry<K,V> e = table[0]; e != null; e = e.next) {
              if (e.key == null)
                  return e.value;
          }
          return null;
      }

remove():删除元素

在这里 public V remove(Object key) {
          Entry<K,V> e = removeEntryForKey(key);
          return (e == null ? null : e.value);
      }
  
      
      final Entry<K,V> removeEntryForKey(Object key) {
          if(size == 0){
              return null;
          }
          
          int hash = (key == null) ? 0 : hash(key.hashCode());
          int i = indexFor(hash, table.length);
          Entry<K,V> prev = table[i];
          Entry<K,V> e = prev;
  
          while (e != null) {
              Entry<K,V> next = e.next;
              Object k;
              if (e.hash == hash &&
                  ((k = e.key) == key || (key != null && key.equals(k)))) {
                  modCount++;
                  size--;
                  if (prev == e)
                      table[i] = next;
                  else
                      prev.next = next;
                  e.recordRemoval(this);
                  return e;
              }
              prev = e;
              e = next;
          }
  
          return e;
      }


HashMap方法总结:

1.int size():map中存储键值对个数

2.boolean isEmpty():判断集合是否为空

3.boolean containsKey(Object key):判断键是否存在

4.boolean containsValue(Object value):判断值是否存在

5.get():通过键获取值;通过Key找到value

6.remove():删除元素(操作key值)

7.void putAll(Map<? extends K, ? extends V> m):将map集合添加至该集合中

8.void clear():清除集合

HashMap特点总结:
数据具有无序性,键值都无序;
键Key不能重复,值value可以重复;
键可以为null,值可以为null;
HashMap是通过"拉链法"实现的哈希表。它包括几个重要的成员变量:table, size, threshold, loadFactor, modCount。
   table是一个Entry[]数组类型,而Entry实际上就是一个单向链表。哈希表的"key-value键值对"都是存储在Entry数组中的。
   size是HashMap的大小,它是HashMap保存的键值对的数量。
   threshold是HashMap的阈值,用于判断是否需要调整HashMap的容量。threshold的值=“容量*加载因子”,当HashMap中存储数据的数量达到 threshold时,就需要将HashMap的容量加倍。
   loadFactor就是加载因子。
   modCount是用来实现fail-fast机制的。
底层数据结构是数组+链表实现
对Map的遍历:三种遍历方式
通过键值对遍历;
先将HashMap实例转化为set实例(类型map.entry<>)

通过键来遍历;仅仅对键进行访问:hashMap.keySet().iterator()

通过值来对map集合进行遍历;hashMap.values().iterator()

//三种对hashmap的遍历方式:
        //通过键值对遍历; 先将HashMap实例转化为Set实例(类型Map.Entry<>)
        //Iterator<> 里返回的数据类型是next()的返回类型;
        Iterator<Map.Entry<String,String>> iterator = hashMap.entrySet().iterator();
        while (iterator.hasNext()){
            Map.Entry<String,String> next = iterator.next();
            String key = next.getKey();
            String value = next.getValue();
            System.out.print(key + " " + value + " ");
        }
        System.out.println();
        //通过键进行遍历  仅仅对键进行访问
        Iterator<String> iterator2 = hashMap.keySet().iterator();
        while (iterator2.hasNext()){
            String next = iterator2.next();
            System.out.print(next + " ");
        }
        System.out.println();
        //通过值来对map集合进行遍历
        //System.out.println(hashMap.values());

        Iterator<String> iterator1 = hashMap.values().iterator();
        while (iterator1.hasNext()) {
            String next = iterator1.next();
            System.out.print(next + " ");
        }
        System.out.println();
    }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值