java性能优化读书笔记之三《程序优化===集合优化(Map)》

1. 数据结构

参考博客数据结构
map概述
这里写图片描述

map的数据结构为key(键)和value(值)键值对的模式。内部原理是基于数组与单向链表的。其操作是对数组和单向链表进行增加、删除、修改、遍历。

HashMap和Hashtable不同之处在于Hashtable允许key或者value为空,再者Hashtable是线程安全的。

2. hashMap结构图(哈希表)

这里写图片描述
纵向为数组、横向为单向链表

使用场景
1、需要线程安全的,请使用Hashtable。
2、允许key或者value为空请使用Hashtable。
3、追求程序性能的,是使用HahsMap

3. HashMap源码分析

了解源码之前,请先学习一下数据结构中的哈希表以及算列法。
数组的特点是:寻址容易,插入和删除困难;而链表的特点是:寻址困难,插入和删除容易。那么我们能不能综合两者的特性,做出一种寻址容易,插入删除也容易的数据结构?答案是肯定的,这就是我们要提起的哈希表,哈希表有多种不同的实现方法,我接下来解释的是最常用的一种方法——拉链法,我们可以理解为“链表的数组。

HashMap无惨构造方法

  /**
     * Constructs an empty <tt>HashMap</tt> with the default initial capacity
     * (16) and the default load factor (0.75).
     */
    public HashMap() {
        //loadFactor 负载因子默认为 0.75f
        this.loadFactor = DEFAULT_LOAD_FACTOR;
        //threshold  临界值 = 默认初始容量 * 负载因子
        threshold = (int)(DEFAULT_INITIAL_CAPACITY * DEFAULT_LOAD_FACTOR);
        //默认初始容量(16)
        table = new Entry[DEFAULT_INITIAL_CAPACITY];
        init();
    }

从源码获取HashMap的容器容量大小为16, 扩容的临界值为12。

hashMap的put方法

    * Associates the specified value with the specified key in this map.
     * If the map previously contained a mapping for the key, the old
     * value is replaced.
     *
     * @param key key with which the specified value is to be associated
     * @param value value to be associated with the specified key
     * @return the previous value associated with <tt>key</tt>, or
     *         <tt>null</tt> if there was no mapping for <tt>key</tt>.
     *         (A <tt>null</tt> return can also indicate that the map
     *         previously associated <tt>null</tt> with <tt>key</tt>.)
     */
    public V put(K key, V value) {
        //key可以为空
        if (key == null)
            return putForNullKey(value);
        //取得key的hashCode
        int hash = hash(key.hashCode());
        //取得元素在数组中的位置或者是内存中的位置
        int i = indexFor(hash, table.length);
        //解决hash冲突,新插入的元素的数组位置如果已经被其他元素占用了,那么就使用单向链表。占用的元素链表下一个指向新插入的元素
        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++;
        //hash不冲突,新元素则添加到指定的数组位置
        addEntry(hash, key, value, i);
        return null;
    }

根据散列法获取数组的位置

 /**
     * Returns index for hash code h.
     */
    static int indexFor(int h, int length) {
        //等同于h%length,但是不等效。h%length需要调用cpu计算。效率更低
        return h & (length-1);
    }

HashMap数组扩容

/**
     * Adds a new entry with the specified key, value and hash code to
     * the specified bucket.  It is the responsibility of this
     * method to resize the table if appropriate.
     *
     * Subclass overrides this to alter the behavior of put method.
     */

    void addEntry(int hash, K key, V value, int bucketIndex) {
    Entry<K,V> e = table[bucketIndex];
        //在指定的数组下标位置添加新元素
        table[bucketIndex] = new Entry<K,V>(hash, key, value, e);
        //判断添加元素后的容器容量大于临界值。则需要数组扩容
        if (size++ >= threshold)
            //扩容的容器容量= 2 * 数组长度
            resize(2 * table.length);
    }



    /**
     * Rehashes the contents of this map into a new array with a
     * larger capacity.  This method is called automatically when the
     * number of keys in this map reaches its threshold.
     *
     * If current capacity is MAXIMUM_CAPACITY, this method does not
     * resize the map, but sets threshold to Integer.MAX_VALUE.
     * This has the effect of preventing future calls.
     *
     * @param newCapacity the new capacity, MUST be a power of two;
     *        must be greater than current capacity unless current
     *        capacity is MAXIMUM_CAPACITY (in which case value
     *        is irrelevant).
     */
    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];
        //将旧的数据内容copy扩容后新的数组
        transfer(newTable);
        table = newTable;
        //重新计算临界值 = 数组长度 * 负载因子
        threshold = (int)(newCapacity * loadFactor);
    }

4. map优化点

1、负载因子本身就是在空间和时间之间的折衷。当我使用较小的负载因子时,虽然降低了冲突的可能性,使得单个链表的长度减小了,加快了访问和更新的速度,但是它占用了更多的空间,使得数组中的大部分空间没有得到利用,元素分布比较稀疏,同时由于Map频繁的调整大小,可能会降低性能。但是如果负载因子过大,会使得元素分布比较紧凑,导致产生冲突的可能性加大,从而访问、更新速度较慢。所以我们一般推荐不更改负载因子的值,采用默认值0.75.

HashMap优化代码
暂无,后续添加

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值