美文网首页
HashMap源码

HashMap源码

作者: 故江 | 来源:发表于2019-02-24 21:32 被阅读11次

    eg:

    HashMap<Integer,String> hashMap = new HashMap<>();

    hashMap.put(1,"QGS");

    System.out.println("QGS"+hashCode());

    Toast.makeText(this,hashMap.get(1)+"    "+hashCode(),Toast.LENGTH_SHORT).show();

    HashMap 的实现不是同步的,这意味着它不是线程安全的。它的key、value都可以为null。此外,HashMap中的映射不是有序的,

    HashMap的实例有两个参数:"初始容量"和"加载因子",容量 是哈希表中桶的数量,初始容量 只是哈希表在创建时的容量。加载因子 是哈希表在其容量自动增加之前可以达到多满的一种尺度.当哈希表中的条目数超出了加载因子与当前容量的乘积时,则要对该哈希表进行 rehash 操作(即重建内部数据结构),从而哈希表将具有大约两倍的桶数。

    默认加载因子是 0.75,(static final float DEFAULT_LOAD_FACTOR = 0.75f;)

    HashMap是以<key,value>键值对进行存储的

    如果HashMap是以键值对进行存储的,那么它使用的数据结构又是什么?

    (根据ArrayList和LinkedList)

    ArrayList  数组    特性:查找快

    LinkedList  链表  特性:增删快(不是单向链表,而是双向链表)

    HashMap的数组类型用Node表示(Node<key,value>[])

    ++size记录数组自己使用的大小

    threshold : 键值对的数量*0.75(默认加载因子)

    注 : 数组大小size不能大于totalsize

    为什么不用链表表示?

    当大小过长是,运行速度或效率会变低.Jdk1.8时,如果链表结构长度大于某个值,就将其结构改成红黑树(红黑树(Red Black Tree) 是一种自平衡二叉查找树,是在计算机科学中用到的一种数据结构,典型的用途是实现关联数组).

    Node节点存在哪里?

    利用key值计算Node.在Object类中找hashCode,hashCode返回值为int类型,拿到key.hashCode(存在数组的位置),hash(key)得到具体值.

    一个int是几位数:

    四个字节,一个字节为8为数,总计32位数.

    HashMap的API

    void                clear()

    Object              clone()boolean              containsKey(Object key)boolean              containsValue(Object value)

    Set<Entry<K, V>>    entrySet()

    V                    get(Object key)boolean              isEmpty()

    Set<K>              keySet()

    V                    put(K key, V value)void                putAll(Map<? extends K, ? extends V> map)

    V                    remove(Object key)int                  size()

    Collection<V>        values()

    HashMap与Map的关系:

    HashMap源码解析:

    // 默认的初始容量是16,必须是2的幂。

    static final int DEFAULT_INITIAL_CAPACITY = 16;

    // 最大容量(必须是2的幂且小于2的30次方,传入容量过大将被这个值替换)static final int MAXIMUM_CAPACITY = 1 << 30;

    // 默认加载因子static final float DEFAULT_LOAD_FACTOR = 0.75f;

    // 存储数据的Entry数组,长度是2的幂。

    // HashMap是采用拉链法实现的,每一个Entry本质上是一个单向链表transient Entry[] table;

    // HashMap的大小,它是HashMap保存的键值对的数量transient int size;

    // HashMap的阈值,用于判断是否需要调整HashMap的容量(threshold = 容量*加载因子)int threshold;

    // 加载因子实际大小final float loadFactor;

    // HashMap被改变的次数 transient volatile int modCount;

    HashMap的构造函数:

    1 // 默认构造函数。 2 public HashMap() { 3    // 设置“加载因子” 4    this.loadFactor = DEFAULT_LOAD_FACTOR; 5    // 设置“HashMap阈值”,当HashMap中存储数据的数量达到threshold时,就需要将HashMap的容量加倍。 6    threshold = (int)(DEFAULT_INITIAL_CAPACITY * DEFAULT_LOAD_FACTOR); 7    // 创建Entry数组,用来保存数据 8    table = new Entry[DEFAULT_INITIAL_CAPACITY]; 9    init();10 }11 12 // 指定“容量大小”和“加载因子”的构造函数13 public HashMap(int initialCapacity, float loadFactor) {14    if (initialCapacity < 0)15        throw new IllegalArgumentException("Illegal initial capacity: " +16                                            initialCapacity);17    // HashMap的最大容量只能是MAXIMUM_CAPACITY18    if (initialCapacity > MAXIMUM_CAPACITY)19        initialCapacity = MAXIMUM_CAPACITY;20    if (loadFactor <= 0 || Float.isNaN(loadFactor))21        throw new IllegalArgumentException("Illegal load factor: " +22                                            loadFactor);23 24    // Find a power of 2 >= initialCapacity25    int capacity = 1;26    while (capacity < initialCapacity)27        capacity <<= 1;28 29    // 设置“加载因子”30    this.loadFactor = loadFactor;31    // 设置“HashMap阈值”,当HashMap中存储数据的数量达到threshold时,就需要将HashMap的容量加倍。32    threshold = (int)(capacity * loadFactor);33    // 创建Entry数组,用来保存数据34    table = new Entry[capacity];35    init();36 }37 38 // 指定“容量大小”的构造函数39 public HashMap(int initialCapacity) {40    this(initialCapacity, DEFAULT_LOAD_FACTOR);41 }42 43 // 包含“子Map”的构造函数44 public HashMap(Map<? extends K, ? extends V> m) {45    this(Math.max((int) (m.size() / DEFAULT_LOAD_FACTOR) + 1,46                  DEFAULT_INITIAL_CAPACITY), DEFAULT_LOAD_FACTOR);47    // 将m中的全部元素逐个添加到HashMap中48    putAllForCreate(m);49 }

    HashMap的主要对外接口

    1. clear()

    clear()的作用是清空HaspMap.他是通过将所有的元素设为null来实现.

    1 public void clear() {2    modCount++;3    Entry[] tab = table;4    for (int i = 0; i < tab.length; i++)5        tab[i] = null;6    size = 0;7 }

    2.containsKey()

    containsKey()的作用是判断HashMap是否包含key.

    public boolean containsKey(Object key) {

        return getEntry(key) != null;

    }

    3.containsValue()

    containsValue()的作用是判断HAshMap是否包含值为"value"的元素

    1 public boolean containsValue(Object value) { 2    // 若“value为null”,则调用containsNullValue()查找 3    if (value == null) 4        return containsNullValue(); 5  6    // 若“value不为null”,则查找HashMap中是否有值为value的节点。 7    Entry[] tab = table; 8    for (int i = 0; i < tab.length ; i++) 9        for (Entry e = tab[i] ; e != null ; e = e.next)10            if (value.equals(e.value))11                return true;12    return false;13 }

    4. entrySet(),value(),keySet()

    1 // 返回“HashMap的Entry集合” 2 public Set<Map.Entry<K,V>> entrySet() { 3    return entrySet0(); 4 } 5  6 // 返回“HashMap的Entry集合”,它实际是返回一个EntrySet对象 7 private Set<Map.Entry<K,V>> entrySet0() { 8    Set<Map.Entry<K,V>> es = entrySet; 9    return es != null ? es : (entrySet = new EntrySet());10 }11 12 // EntrySet对应的集合13 // EntrySet继承于AbstractSet,说明该集合中没有重复的EntrySet。14 private final class EntrySet extends AbstractSet<Map.Entry<K,V>> {15    public Iterator<Map.Entry<K,V>> iterator() {16        return newEntryIterator();17    }18    public boolean contains(Object o) {19        if (!(o instanceof Map.Entry))20            return false;21        Map.Entry<K,V> e = (Map.Entry<K,V>) o;22        Entry<K,V> candidate = getEntry(e.getKey());23        return candidate != null && candidate.equals(e);24    }25    public boolean remove(Object o) {26        return removeMapping(o) != null;27    }28    public int size() {29        return size;30    }31    public void clear() {32        HashMap.this.clear();33    }34 }

    5.get()

    get()的作用是获取key对应的valuezhi

    1 public V get(Object key) { 2    if (key == null) 3        return getForNullKey(); 4    // 获取key的hash值 5    int hash = hash(key.hashCode()); 6    // 在“该hash值对应的链表”上查找“键值等于key”的元素 7    for (Entry<K,V> e = table[indexFor(hash, table.length)]; 8          e != null; 9          e = e.next) {10        Object k;11        if (e.hash == hash && ((k = e.key) == key || key.equals(k)))12            return e.value;13    }14    return null;15 }

    6.put()

    put()的作用是多外提供接口,让HashMap对象可以通过put()将"key-value"存放到HashMap中

    7.putAll()

    putAll()的作用是将"m"的全部元素都添加到HashMap中

    1 public void putAll(Map<? extends K, ? extends V> m) { 2    // 有效性判断 3    int numKeysToBeAdded = m.size(); 4    if (numKeysToBeAdded == 0) 5        return; 6  7    // 计算容量是否足够, 8    // 若“当前实际容量 < 需要的容量”,则将容量x2。 9    if (numKeysToBeAdded > threshold) {10        int targetCapacity = (int)(numKeysToBeAdded / loadFactor + 1);11        if (targetCapacity > MAXIMUM_CAPACITY)12            targetCapacity = MAXIMUM_CAPACITY;13        int newCapacity = table.length;14        while (newCapacity < targetCapacity)15            newCapacity <<= 1;16        if (newCapacity > table.length)17            resize(newCapacity);18    }19 20    // 通过迭代器,将“m”中的元素逐个添加到HashMap中。21    for (Iterator<? extends Map.Entry<? extends K, ? extends V>> i = m.entrySet().iterator(); i.hasNext(); ) {22        Map.Entry<? extends K, ? extends V> e = i.next();23        put(e.getKey(), e.getValue());24    }25 }

    8.remove()

    remove()的作用是删除键wei"key"的元素

    1 public V remove(Object key) { 2    Entry<K,V> e = removeEntryForKey(key); 3    return (e == null ? null : e.value); 4 } 5  6  7 // 删除“键为key”的元素 8 final Entry<K,V> removeEntryForKey(Object key) { 9    // 获取哈希值。若key为null,则哈希值为0;否则调用hash()进行计算10    int hash = (key == null) ? 0 : hash(key.hashCode());11    int i = indexFor(hash, table.length);12    Entry<K,V> prev = table[i];13    Entry<K,V> e = prev;14 15    // 删除链表中“键为key”的元素16    // 本质是“删除单向链表中的节点”17    while (e != null) {18        Entry<K,V> next = e.next;19        Object k;20        if (e.hash == hash &&21            ((k = e.key) == key || (key != null && key.equals(k)))) {22            modCount++;23            size--;24            if (prev == e)25                table[i] = next;26            else27                prev.next = next;28            e.recordRemoval(this);29            return e;30        }31        prev = e;32        e = next;33    }34 35    return e;36 }

    HashMap继承于AbstractMap,实现Map、Cloneable、java.io.Serializable接口.

    Cloneable和Serializable都是一个接口

    cloneHashMap对象并返回

    1 // 克隆一个HashMap,并返回Object对象 2 public Object clone() { 3    HashMap<K,V> result = null; 4    try { 5        result = (HashMap<K,V>)super.clone(); 6    } catch (CloneNotSupportedException e) { 7        // assert false; 8    } 9    result.table = new Entry[table.length];10    result.entrySet = null;11    result.modCount = 0;12    result.size = 0;13    result.init();14    // 调用putAllForCreate()将全部元素添加到HashMap中15    result.putAllForCreate(this);16 17    return result;18 }

    Serializable分别实现了串行读取/写入功能

    遍历HashMap的键值对

    // 假设map是HashMap对象

    // map中的key是String类型,value是Integer类型Integer integ = null;

    Iterator iter = map.entrySet().iterator();while(iter.hasNext()) {

        Map.Entry entry = (Map.Entry)iter.next();

        // 获取key    key = (String)entry.getKey();

            // 获取value    integ = (Integer)entry.getValue();

    }

    遍历HashMap的键

    // 假设map是HashMap对象

    // map中的key是String类型,value是Integer类型String key = null;

    Integer integ = null;

    Iterator iter = map.keySet().iterator();while (iter.hasNext()) {

            // 获取key    key = (String)iter.next();

            // 根据key,获取value    integ = (Integer)map.get(key);

    }

    遍历HashMap的值

    // 假设map是HashMap对象

    // map中的key是String类型,value是Integer类型Integer value = null;

    Collection c = map.values();

    Iterator iter= c.iterator();while (iter.hasNext()) {

        value = (Integer)iter.next();

    }

    相关文章

      网友评论

          本文标题:HashMap源码

          本文链接:https://www.haomeiwen.com/subject/bcfiyqtx.html