hashmap的扩容因子是0.75 原因 参考:HashMap默认加载因子为什么选择0.75?(阿里)

1.    HashMap概述

HashMap是基于哈希表的Map接口的非同步实现。此实现提供所有可选的映射操作,并允许使用null值和null键。此类不保证映射的顺序,特别是它不保证该顺序恒久不变。

2.    HashMap的数据结构

在java编程语言中,最基本的结构就是两种,一个是数组,另外一个是模拟指针(引用),所有的数据结构都可以用这两个基本结构来构造的,HashMap也不例外。HashMap实际上是一个“链表散列”的数据结构,即数组和链表的结合体。这样的结构结合了链表在增删方面的高效和数组在寻址上的优势

hashmap结构:哈希表是由数组+链表组成的,数组的默认长度为16 ,注意 hashtable的数组的默认长度是11(可以自动变长。在构造HashMap的时候也可以指定一个长度),数组里每个元素存储的是一个链表的头结点。而组成链表的结点其实就是hashmap内部定义的一个类:Entity。Entity包含三个元素:key,value和指向下一个Entity的next

3.  HashMap的存取

这些元素是按照什么样的规则存储到数组中呢。一般情况是通过hash(key)%(len-1)获得,也就是元素的key的哈希值对数组长度取模得到。比如上述哈希表中,12%16=12,28%16=12,108%16=12,140%16=12。所以12、28、108以及140都存储在数组下标为12的位置。

HashMap的存储--put:

int hash = key.hashCode(); // 这个hashCode方法这里不详述,只要理解每个key的hash是一个固定的int值
int index = hash %( Entry[].length-1);
table[index] = value;//假定存储链表头结点的数组名为table

用table[index]表示通过hash值计算出来的、元素需要存储在数组中的位置。先判断该位置上有没有存有Entity,没有的话就创建一个Entity<k,v>对象,在该位置上插入,插入结束;如果有的话,通过链表的遍历方式去逐个遍历,通过equals方法将key和已有的key进行比较,看看有没有已经存在的key,有的话用新的value替换老的value;如果没有,则在table[index]插入该Entity,把原来在table[index]位置上的Entity赋值给新的 Entity的next,也即,新的Entity插入(put)的位置永远是在链表的最前面(百度面试),这样插入结束。

打个比方, 第一个键值对A进来,通过计算其key的hash得到的index=0,记做:table[0] = A。一会后又进来一个键值对B,通过计算其index也等于0,现在怎么办?HashMap会这样做:B.next = A,table[0] = B,如果又进来C,index也等于0,那么C.next = B,table[0] = C;这样我们发现index=0的地方其实存取了A,B,C三个键值对,他们通过next这个属性链接在一起。

注:null key总是存放在Entry[]数组的第一个元素。

扩展:

按照散列函数的定义,如果两个对象相同,即obj1.equals(obj2)=true,则它们的hashCode必须相同,但如果两个对象不同,则它们的hashCode不一定不同。

如果两个不同对象的hashcode相同,就称为冲突。冲突会导致操作哈希表的时间开销增大,所以尽量定义好的hashCode()方法,能加快哈希表的操作。覆盖了equals方法之后一定要覆盖hashCode方法,原因很简单,比如,String a = new String(“abc”);String b = new String(“abc”);如果不覆盖hashCode的话,那么a和b的hashCode就会不同,把这两个类当做key存到HashMap中的话就 会出现问题,就会和key的唯一性相矛盾。

HashMap的读取--get:

先定位到数组元素,再遍历该元素处的链表

public V get(Object key) {
if (key == null)
return getForNullKey();
int hash = hash(key.hashCode());
//先定位到数组元素,再遍历该元素处的链表
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.equals(k)))
return e.value;//显然,在寻找目标元素的时候,除了对比通过key计算出来的hash值,还会用双等或equals方法对key本身来进行比较,两者都为true时才会返回这个元素
}
return null;
}

遍历规则:如下:

import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry; public class abc_test { public static void main(String[] args) {
// TODO Auto-generated method stub Map<String,String> map=new HashMap<String,String>();
map.put("1", "value1");
map.put("2", "value2");
map.put("3", "value3");
map.put("4", "value4"); //第一种:普通使用,二次取值
System.out.println("\n通过Map.keySet遍历key和value:");
for(String key:map.keySet())
{
System.out.println("Key: "+key+" Value: "+map.get(key));
} //第二种:推荐,尤其是容量大时
System.out.println("\n通过Map.entrySet遍历key和value");
for(Map.Entry<String, String> entry: map.entrySet())
{
System.out.println("Key: "+ entry.getKey()+ " Value: "+entry.getValue());
} //第三种
System.out.println("\n通过Map.values()遍历所有的value,但不能遍历key");
for(String v:map.values())
{
System.out.println("The value is "+v);
} //第四种
System.out.println("\n通过Iterator 遍历Map.entrySet的key和value: ");
Iterator map1it=map.entrySet().iterator();
while(map1it.hasNext())
{
Map.Entry<String, String> entry=(Entry<String, String>) map1it.next();
System.out.println("Key: "+entry.getKey()+" Value: "+entry.getValue());
} //第五种
System.out.println("\n通过iterator 遍历Map.keySet的key和value: ");
Iterator keyIter=map.keySet().iterator();
while(keyIter.hasNext())
{
String key=(String)keyIter.next();
System.out.println("Key: "+key+" Value: "+map.get(key));
} } private static void print(Collection<Person> set)
{
Iterator<Person> it = set.iterator();
while (it.hasNext())
{
Person p = it.next();
System.out.println(p.toString());
}
} } class test { public int d;
public String name;
public int getD() {
return d;
}
public void setD(int d) {
this.d = d;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public boolean equals(Object obj) {
// TODO Auto-generated method stub
return super.equals(obj);
} } class Person
{
public Person(String name, int age)
{
this.name = name;
this.age = age;
} private String name;
private int age; public String getName()
{
return name;
} public void setName(String name)
{
this.name = name;
} public int getAge()
{
return age;
} public void setAge(int age)
{
this.age = age;
} public String toString()
{
return "{" + name + ", " + age + "}";
} } class joinDemo implements Runnable{ @Override
public void run() {
// TODO Auto-generated method stub for(int i=0;i<10;i++){ System.out.println("线程1第"+i+"次执行");
} } }

结果为:

通过Map.keySet遍历key和value:
Key: 1 Value: value1
Key: 2 Value: value2
Key: 3 Value: value3
Key: 4 Value: value4 通过Map.entrySet遍历key和value
Key: 1 Value: value1
Key: 2 Value: value2
Key: 3 Value: value3
Key: 4 Value: value4 通过Map.values()遍历所有的value,但不能遍历key
The value is value1
The value is value2
The value is value3
The value is value4 通过Iterator 遍历Map.entrySet的key和value:
Key: 1 Value: value1
Key: 2 Value: value2
Key: 3 Value: value3
Key: 4 Value: value4 通过iterator 遍历Map.keySet的key和value:
Key: 1 Value: value1
Key: 2 Value: value2
Key: 3 Value: value3
Key: 4 Value: value4

参考:遍历HashMap的四种方法

参考:HashMap的实现原理--链表散列

HashMap的实现原理--链表散列的更多相关文章

  1. Java中HashMap的实现原理

    最近面试中被问及Java中HashMap的原理,瞬间无言以对,因此痛定思痛觉得研究一番. 一.Java中的hashCode和equals 1.关于hashCode hashCode的存在主要是用于查找 ...

  2. HashMap 的实现原理解析(转载)

    HashMap 概述 HashMap 是基于哈希表的 Map 接口的非同步实现.此实现提供所有可选的映射操作,并允许使用 null 值和 null 键.此类不保证映射的顺序,特别是它不保证该顺序恒久不 ...

  3. HashMap 的实现原理(1.8)

    详见:https://blog.csdn.net/richard_jason/article/details/53887222 HashMap概述 1.初始容量默认为16 最大为2的30次方,负载因子 ...

  4. HashMap 的实现原理(1.7)

    参考 :http://wiki.jikexueyuan.com/project/java-collection/hashmap.html https://blog.csdn.net/w22981192 ...

  5. 深入Java集合学习系列:HashMap的实现原理

    1.    HashMap概述: HashMap是基于哈希表的Map接口的非同步实现.此实现提供所有可选的映射操作,并允许使用null值和null键.此类不保证映射的顺序,特别是它不保证该顺序恒久不变 ...

  6. 浅谈HashMap的实现原理

    1.    HashMap概述: HashMap是基于哈希表的Map接口的非同步实现.此实现提供所有可选的映射操作,并允许使用null值和null键.此类不保证映射的顺序,特别是它不保证该顺序恒久不变 ...

  7. [转]HashMap的实现原理

    1.    HashMap概述: HashMap是基于哈希表的Map接口的非同步实现.此实现提供所有可选的映射操作,并允许使用null值和null键.此类不保证映射的顺序,特别是它不保证该顺序恒久不变 ...

  8. Java:HashMap的实现原理(JDK1.8)

    1.    HashMap概述: HashMap是基于哈希表的Map接口的非同步实现.此实现提供所有可选的映射操作,并允许使用null值和null键.此类不保证映射的顺序,特别是它不保证该顺序恒久不变 ...

  9. HashMap的底层原理 cr:csdn:zhangshixi

    1.    HashMap概述: HashMap是基于哈希表的Map接口的非同步实现.此实现提供所有可选的映射操作,并允许使用null值和null键.此类不保证映射的顺序,特别是它不保证该顺序恒久不变 ...

随机推荐

  1. lazy-load-img.js 源码 学习笔记及原理说明

    lazy-load-img.js? 1. 什么鬼? 一个轻量级的图片懒加载,我个人很是喜欢. 2. 有什么优势? 1.原生js开发,不依赖任何框架或库 2.支持将各种宽高不一致的图片,自动剪切成默认图 ...

  2. day83

    今日内容 rest_framework序列化 首先序列化需要对写一个类继承serializers.Serializer 方式一:在models的publish写一个__str__方法返回出版社名字 p ...

  3. [10] AOP的注解配置

    1.关于配置文件 首先在因为要使用到扫描功能,所以xml的头文件中除了引入bean和aop之外,还要引入context才行: <?xml version="1.0" enco ...

  4. linux安装jdk与配置-centos7版本

    1.Linux安裝jdk 1.如果電腦沒有wget命令的,先使用yum安裝wget命令. eg: yum install wget 2.安裝好后就可以直接使用wget命令去下載jdk. 附:打開官網連 ...

  5. mysql 5.7 版本的安装

    目录 一.概述 二.MySQL安装 三.安装成功验证 四.NavicatforMySQL下载及使用 一.概述 MySQL版本:5.7.17 下载地址:http://rj.baidu.com/soft/ ...

  6. 计算2个时间之间经过多少Ticks

    Ticks是一个周期,存储的是一百纳秒,换算为秒,一千万分之一秒.我们需要计算2个时间之间,经过多少Ticks,可以使用下面的方法来实现,使用2个时间相减. 得到结果为正数,是使用较晚的时间减去较早的 ...

  7. 常见 HTTP/FTP/WebSocket 错误代码大全 - 转

    HTTP 1xx消息 这一类型的状态码,代表请求已被接受,需要继续处理.这类响应是临时响应,只包含状态行和某些可选的响应头信息,并以空行结束.由于HTTP/1.0协议中没有定义任何1xx状态码,所以除 ...

  8. 解决PowerDesigner 16 Generate Datebase For Sql2005/2008 对象名sysproperties无效的问题

    在PowerDesigner 16 中生成的sql语句,在执行的时候报错:对象名sysproperties 无效的错误;造成此问题的原因是由于Sql 2005.2008 删除了系统表 sysprope ...

  9. 性能调优之vmstat命令

    vmstat是Virtual Meomory Statistics(虚拟内存统计)的缩写,可对操作系统的虚拟内存.进程.IO读写.CPU活动等进行监视.它是对系统的整体情况进行统计,不足之处是无法对某 ...

  10. 记一次MongoDB裸奔

    导言 大意失荆州,裸奔的 MongoDB 被黑了.虽然并不是什么非常重要的数据,但也给自己敲响的一个警钟.虽然我们平时不容易接触到数据安全,但我们在开发,部署项目的时候,一定要养成良好的安全意识. 根 ...