[Java]JDK源码学习(3)HashMap

来源:互联网 发布:python与php哪个好学 编辑:程序博客网 时间:2024/05/18 00:13

首先我们知道HashMap是线程不安全的,所以要避免多个线程共享操作

HashMap的数据结构

HashMap主要是通过数组(transient Entry[] table)来存储数据的,通过key得到hash值后找到数组中相对应的位置,如果有冲突,则是用链表来解决的,请看Entry的属性

final K key;
V value;
Entry<K,V> next;
final int hash;

这里的next就是为了哈希冲突而存在的。比如通过哈希运算,一个新元素应该在数组的第10个位置,但是第10个位置已经有Entry,那么好吧,将新加的元素也放到第10个位置,将第10个位置的原有Entry赋值给当前新加的 Entry的next属性。数组存储的是链表,链表是为了解决哈希冲突的,这一点要注意。


先看几个HashMap里的字段

static final int MAXIMUM_CAPACITY = 1 << 30;

DEFAULT_LOAD_FACTOR = 0.75;

static final int DEFAULT_INITIAL_CAPACITY = 16;

:这是用来存储数据的,这个数组同样不会序列化,具体不再累述

transient int size:表示当前存储数据大小

int threshold:阈值,hashMap存放内容数量的临界点,当存放量大于这个值的时候,就需要将table进行扩张,新建一个两倍大的数组,并将老的元素移过去。threshold = (int)(capacity * loadFactor)

final float loadFactor:装载因子,在构造HashMap时指定,用来确定threshold

transient volatile int modCount:记录hashMap结构变化的次数,使用在hashMap的fail-fast机制中(当某个线程获取map的游标后,另一个线程对map做了结构修改的操作,那么原先准备遍历的线程会抛出ConcurrentModificationException异常)。这里注意volatile关键词,它代表modCount这个变量在每次被线程访问时,都强迫从共享内存中重读该成员变量的值。


再来看HashMap的构造函数

构造函数1:

    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)            capacity <<= 1;        this.loadFactor = loadFactor;        threshold = (int)(capacity * loadFactor);        table = new Entry[capacity];        init();    }
看到注释了吗?capacity是初始容量,而不是initialCapacity,例如执行new HashMap(9,0.75);那么HashMap的初始容量是16,而不是9

构造函数2:

  public HashMap(int initialCapacity) {       this(initialCapacity, DEFAULT_LOAD_FACTOR);  }
使用指定的初始容量,装载因子为DEFAULT_LOAD_FACTOR,调用构造函数1来完成

构造函数3:

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

初始容量和装载因子均为默认值

构造函数4:

    public HashMap(Map<? extends K, ? extends V> m) {        this(Math.max((int) (m.size() / DEFAULT_LOAD_FACTOR) + 1,                      DEFAULT_INITIAL_CAPACITY), DEFAULT_LOAD_FACTOR);        putAllForCreate(m);    }

从现有的map构造新的hashMap,初始容量用原map的容量和默认容量中大的那个值

下面让我们看看几个重要的功能实现

    public V get(Object key) {        if (key == null)            return getForNullKey();        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;    }

get函数通过输入key得到value,先通过hash,然后通过indexFor函数得到这个key在table的位置,再遍历链表得到结果

    static int indexFor(int h, int length) {        return h & (length-1);    }






原创粉丝点击