美文网首页
HashMap学习

HashMap学习

作者: 剑客kb | 来源:发表于2019-01-06 17:42 被阅读0次

使用JDK1.7

构造方法

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;
        threshold = initialCapacity;
        init();
    }

initialCapacity默认16,loadFactor默认0.75,init()是便于子类初始化重写;MAXIMUM_CAPACITY最大2的30次方

put操作

public V put(K key, V value) {
        if (table == EMPTY_TABLE) {
            inflateTable(threshold);
        }
        if (key == null)
            return putForNullKey(value);
        int hash = hash(key);
        int i = indexFor(hash, table.length);
        for (Entry<K,V> e = table[i]; e != null; e = e.next) {
            Object k;
            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;
    }

首先第一个元素进入之后,先调用inflateTable方法

private void inflateTable(int toSize) {
        // Find a power of 2 >= toSize
        int capacity = roundUpToPowerOf2(toSize);

        threshold = (int) Math.min(capacity * loadFactor, MAXIMUM_CAPACITY + 1);
        table = new Entry[capacity];
        initHashSeedAsNeeded(capacity);
    }

public static int highestOneBit(int i) {
        // HD, Figure 3-1
        i |= (i >>  1);
        i |= (i >>  2);
        i |= (i >>  4);
        i |= (i >>  8);
        i |= (i >> 16);
        return i - (i >>> 1);
    }

roundUpToPowerOf2是取这个数的二进制形式最左边的最高一位且高位后面全部补零;
可以看到如果key是null,会单独处理

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;
    }

void addEntry(int hash, K key, V value, int bucketIndex) {
        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);
    }

如果table[0]位置存在key=null的元素,则替换value,返回旧的value,否则在0位置添加元素。当现在hashmap中的Entry数大于等于扩容临界值(capacity*load factor)并且index对应的地方没有Entry就扩容hashmap每次扩容的大小为2倍原容量。

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);
    }

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;
            }
        }
    }

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);
    }

final int hash(Object k) {
        int h = hashSeed;
        if (0 != h && k instanceof String) {
            return sun.misc.Hashing.stringHash32((String) k);
        }

        h ^= k.hashCode();

        // This function ensures that hashCodes that differ only by
        // constant multiples at each bit position have a bounded
        // number of collisions (approximately 8 at default load factor).
        h ^= (h >>> 20) ^ (h >>> 12);
        return h ^ (h >>> 7) ^ (h >>> 4);
    }

对一个数进行求余时可以学习indexFor的做法,位运算(&)效率要比代替取模运算(%)高很多,主要原因是位运算直接对内存数据进行操作,不需要转成十进制,因此处理速度非常快。

hash函数:为了对key的hashCode进行扰动计算,防止不同hashCode的高位不同但低位相同导致的hash冲突。简单点说,就是为了把高位的特征和低位的特征组合起来,降低哈希冲突的概率,也就是说,尽量做到任何一位的变化都能对最终得到的结果产生影响

get操作

public V get(Object key) {
        if (key == null)
            return getForNullKey();
        Entry<K,V> entry = getEntry(key);

        return null == entry ? null : entry.getValue();
    }

private V getForNullKey() {
        if (size == 0) {
            return null;
        }
        for (Entry<K,V> e = table[0]; e != null; e = e.next) {
            if (e.key == null)
                return e.value;
        }
        return null;
    }

final Entry<K,V> getEntry(Object key) {
        if (size == 0) {
            return null;
        }

        int hash = (key == null) ? 0 : hash(key);
        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 != null && key.equals(k))))
                return e;
        }
        return null;
    }

可以看到和put操作类似

modCount:修改次数,因为HashMap是线程不安全,如果在迭代的过程中HashMap被其他线程修改了,modCount的数值就会发生变化, 这个时候expectedModCount和ModCount不相等, 迭代器就会抛出ConcurrentModificationException()异常

参考:
https://www.hollischuang.com/archives/2091
https://blog.csdn.net/glory1234work2115/article/details/50825503
https://blog.csdn.net/qq_30788949/article/details/80840097

相关文章

  • Java源码学习--HashMap

    Java源码学习--HashMap 由于HashSet的实现原理是HashMap,所以我们先从HashMap开始学...

  • Java基础_HashMap源码分析

    该篇文章主要从如下几个方面来学习下HashMap HashMap是啥 HashMap的代码实操 HashMap的g...

  • 你真的了解LinkedHashMap吗

    一、前言 LinkedHashMap 继承于 HashMap,因此,建议在学习本篇内容前,先学习 HashMap系...

  • 你真的了解LinkedHashMap吗?进来看看

    一、前言 LinkedHashMap 继承于 HashMap,因此,建议在学习本篇内容前,先学习 HashMap系...

  • Android 面试准备进行曲(数据结构 Map / List)

    Java数据结构 之 HashMap 重温学习1. HashMap2. hash() 方法3. HashMap 的...

  • HashMap学习

    HashMap 这个实现对get和put提供了固定时间的性能迭代完全部集合的时间是与HashMap的容量+键值对的...

  • HashMap学习

    调用如下: HashMap map=new HashMap();map.put("1","1");String ...

  • HashMap学习

    概述 线程非安全,并且允许key与value都为null值,HashTable与之相反,为线程安全,key与val...

  • HashMap学习

    首先HasMap在JDK 1.7 和 1.8是稍有不同的。 简介 HashMap是一个散列桶(数组和链表,1.8还...

  • HashMap学习

    HashMap有两个重要参数:初始容量已经加载因子。这两个参数对HashMap的性能有较大影响。 一些变量说明: ...

网友评论

      本文标题:HashMap学习

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