HashMap—基本结构

前言


       数组的特点是:寻址容易,插入和删除困难,数组利用下标定位,时间复杂度为O(1),插入或删除元素的时间复杂度O(n)。

       链表的特点是:寻址困难,插入和删除容易,链表定位元素时间复杂度O(n),插入或删除元素的时间复杂度O(1)。

那么我们能不能综合两者的特性,做出一种寻址容易,插入删除也容易的数据结构?

HashMap是结合两者优势,这是一种折中的方案。(在java8中引入了红黑树,是对性能的更进一步优化)

HashMap内部结构



HashMap实际上是一个数组,数组里面的每个元素都是一个链表。每个元素在通过put方法放入HashMap中的时候,要按照如下步骤进行:

1.根据该元素自身提供的hashcode计算出散列值,该散列值就是数组的下标

2.将新元素放入该数组位置的链表中

数组定义:

  1. /**
  2. * The table, resized as necessary. Length MUST Always be a power of two.
  3. */
  4. transient Entry[] table;

这是一个数组,transient关键字告诉我们它不会参与序列化。既然是一个数组,总有数目上限,也就意味着如果存入HashMap的元素太多,导致数组大小不能够存放所有的链表的时候,数组大小必须要能够调整。

Entry是什么类型?

 1 static class Entry<K,V> implements Map.Entry<K,V> {
 2         final K key;
 3         V value;
 4         Entry<K,V> next;
 5         final int hash;
 6
 7         /**
 8          * Creates new entry.
 9          */
10         Entry(int h, K k, V v, Entry<K,V> n) {
11             value = v;
12             next = n;
13             key = k;
14             hash = h;
15         }
16         ....
17         public final boolean equals(Object o) {
18             if (!(o instanceof Map.Entry))
19                 return false;
20             Map.Entry e = (Map.Entry)o;
21             Object k1 = getKey();
22             Object k2 = e.getKey();
23             if (k1 == k2 || (k1 != null && k1.equals(k2))) {
24                 Object v1 = getValue();
25                 Object v2 = e.getValue();
26                 if (v1 == v2 || (v1 != null && v1.equals(v2)))
27                     return true;
28             }
29             return false;
30         }
31
32         public final int hashCode() {
33             return (key==null   ? 0 : key.hashCode()) ^
34                    (value==null ? 0 : value.hashCode());
35         }
36         ....

Entry部分源码

这是一个HashMap类的内部静态类。实现了Map.Entry接口。接受两个模板参数K和V。key和hash一旦在构造函数中被初始化,就不可改变,并且由于有next的存在,Entry可以构成一个单向链表。

HashMap 包含如下几个构造器:

  1. HashMap():构建一个初始容量为 16,负载因子为 0.75 的 HashMap。

  2. ashMap(int initialCapacity):构建一个初始容量为 initialCapacity,负载因子为 0.75 的 HashMap。

  3. HashMap(int initialCapacity, float loadFactor):以指定初始容量、指定的负载因子创建一个 HashMap。

HashMap的基础构造器HashMap(int initialCapacity, float loadFactor)带有两个参数,它们是初始容量initialCapacity和负载因子loadFactor。

负载因子loadFactor衡量的是一个散列表的空间的使用程度,负载因子越大表示散列表的装填程度越高,反之愈小。对于使用链表法的散列表来说,查找一个元素的平均时间是O(1+a),因此如果负载因子越大,对空间的利用更充分,然而后果是查找效率的降低;如果负载因子太小,那么散列表的数据将过于稀疏,对空间造成严重浪费。

HashMap的实现中,通过threshold字段来判断HashMap的最大容量:

threshold = (int)(capacity * loadFactor);

结合负载因子的定义公式可知,threshold就是在此loadFactor和capacity对应下允许的最大元素数目,超过这个数目就重新resize,以降低实的负载因子。默认的的负际载因子0.75是对空间和时间效率的一个平衡选择。当容量超出此最大容量时, resize后的HashMap容量是原来容量的两倍.

参考:

http://www.codeceo.com/article/java-hashmap-learn.html

http://blog.csdn.net/csfreebird/article/details/7347026

http://www.cnblogs.com/xwdreamer/archive/2012/05/14/2499339.html

时间: 2024-11-03 22:01:07

HashMap—基本结构的相关文章

Map实现之HashMap(结构及原理)(转)

java.util包中的集合类包含 Java 中某些最常用的类.最常用的集合类是 List 和 Map.List 的具体实现包括 ArrayList 和 Vector,它们是可变大小的列表,比较适合构建.存储和操作任何类型对象元素列表.List 适用于按数值索引访问元素的情形. Map 则提供了一个更通用的元素存储方法.Map 集合类用于存储元素对(称作“键”和“值”),其中每个键映射到一个值.从概念上而言,您可以将 List 看作是具有数值键的 Map.而实际上,除了 List 和 Map 都

HashMap的实现原理和底层结构

哈希表(hash table)也叫散列表,是一种非常重要的数据结构,应用场景及其丰富,许多缓存技术(比如memcached)的核心其实就是在内存中维护一张大的哈希表,而HashMap的实现原理也常常出现在各类的面试题中,重要性可见一斑.本文会对java集合框架中的对应实现HashMap的实现原理进行讲解,然后会对JDK7的HashMap源码进行分析. 一.什么是哈希表 在讨论哈希表之前,我们先大概了解下其他数据结构在新增,查找等基础操作执行性能 数组:采用一段连续的存储单元来存储数据.对于指定下

HashMap和Hashtable

HashMap和Hashtable的区别 HashMap和Hashtable都实现了Map接口,但决定用哪一个之前先要弄清楚它们之间的分别.主要的区别有:线程安全性,同步(synchronization),以及速度. HashMap几乎可以等价于Hashtable,除了HashMap是非synchronized的,并可以接受null(HashMap可以接受为null的键值(key)和值(value),而Hashtable则不行). HashMap是非synchronized,而Hashtable

集合源码分析之 HashMap

一 知识准备 HashMap是基于哈希表的Map接口的非同步实现.此实现提供所有可选的映射操作,并允许使用null值和null键.此类不保证映射的顺序,特别是它不保证该顺序恒久不变. 二  HashMap的数据结构: JDK 7.0及以前 在java编程语言中,最基本的结构就是两种,一个是数组,另外一个是模拟指针(引用),所有的数据结构都可以用这两个基本结构来构造的,HashMap也不例外.HashMap实际上是一个“链表散列”的数据结构,即数组和链表的结合体. 从上图中可以看出,HashMap

Java源码解析之HashMap

一.HashMap类声明: HashMap继承于AbstractMap并且实现了接口Map,Cloneable,Serializable. public class HashMap<K,V> extends AbstractMap<K,V> implements Map<K,V>, Cloneable, Serializable {} 二.HashMap类层次: HashMap实现了三个接口,继承一个抽象类.除此之外我们应该知道Object是所有类的超类.之所以有一个A

HashMap原理-1.7

之所以分两篇文章记录,因为之前一直看的1.7的源码,而且网上很多的都是关于1.7的,今天在1.8上打开源码一看,居然懵了.    没想到1.8的实现变化这么大.所有特地拿一篇文章来记录下. 本章只介绍1.7的情况 1.HashMap存储结构 哈希表是由数组+链表组成的,一个长度为16的数组中,每个元素存储的是一个链表的头结点.那么这些元素是按照什么样的规则存储到数组中呢.一般情况是通过hash(key)%len获得,也就是元素的key的哈希值对数组长度取模得到.比如上述哈希表中,12%16=12

Java HashMap的工作原理(转载)

原文地址:http://www.importnew.com/10620.html 面试的时候经常会遇见诸如:"java中的HashMap是怎么工作的","HashMap的get和put内部的工作原理"这样的问题.本文将用一个简单的例子来解释下HashMap内部的工作原理.首先我们从一个例子开始,而不仅仅是从理论上,这样,有助于更好地理解,然后,我们来看下get和put到底是怎样工作的. 我们来看个非常简单的例子.有一个"国家"(Country)类

(原)HashMap之java8新特性

首先说一下HashMap存储结构,数组.链表.树这三种数据结构形成了hashMap.存储结构下图所示,根据key的hash与table长度确定table位置,同一个位置的key以链表形式存储,超过一定限制链表转为树.数组的具体存取规则是tab[(n-1) & hash],其中tab为node数组,n为数组的长度,hash为key的hash值. //链表中数据的临界值,如果达到8,就进行resize扩展,如果数组大于64则转换为树. static final int TREEIFY_THRESHO

JDK1.8 重识HashMap

摘要 JDK1.8相较于1.7对HashMap做了很大的优化,比如加入了新数据结构红黑树.Hash算法的优化和扩容的优化. 本篇结合这些区别,探索HashMap的结构实现和功能原理. 存储结构-字段 从数据结构来看,HashMap是数组+链表+红黑树实现的,如图所示: HashMap中重要的几个属性(JDK 1.8): 1 static final int MAXIMUM_CAPACITY = 1 << 30; // 所能容纳的Node极限数量 2 static final float DEF