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

来源:互联网 发布:linux查看端口nc 编辑:程序博客网 时间:2024/04/30 21:26

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优化代码
暂无,后续添加

0 0
原创粉丝点击