Java HashMap整理
来源:互联网 发布:幼儿园美工课图片 编辑:程序博客网 时间:2024/05/14 21:44
HashMap
数组和链表的接合体,先根据key找到数组下标,若该key有了value,则把要插入的value放在链表头
Map map = new HashMap();map.put("Rajib Sarma","100");map.put("Rajib Sarma","200");//The value "100" is replaced by "200".map.put("Sazid Ahmed","200");Iterator iter = map.entrySet().iterator();while (iter.hasNext()) { Map.Entry entry = (Map.Entry) iter.next(); Object key = entry.getKey(); Object val = entry.getValue();}
HashTable和HashMap区别
- 继承不同
public class Hashtable extends Dictionary implements Mappublic class HashMap extends AbstractMap implements Map
同步
HashTable是同步的,在多线程并发状态下可以使用
HashMap不同步null值
HashMap允许key、value出现null,HashTable不允许
HashMap中key为null的值只能有一个,value为null的可以有很多。
get方法返回null有两种情况:
1、不存在该建对应的值
2、该键对应的值为null
所以需要使用containsKey判断是否存在某个键,不应用get遍历实现不同
都使用了iterator,历史原因HashTable还使用了enumeration,而且用与代替求模哈希值的使用不同
HashTable使用对象的HashCode,hashMap重新计算对象的hash值内部实现数组大小和扩容方式
HashMap:默认16,以2的指数扩容
HashTable:默认11,以old*2+1扩容
ConcurrentHashMap
实现细节
- ConcurrentHashMap不是用synchronized关键字实现的
ConcurrentHashMap使用segment来分段和管理锁,segment继承自ReentrantLock,因此ConcurrentHashMap使用ReentrantLock来保证线程安全。
HashMap实现原理
HashMap实现原理分析
HashMap工作原理
HashMap是不是所有Object都能作为key?
不是。
1、什么是可变对象
可变对象是指创建后自身状态能改变的对象。换句话说,可变对象是该对象在创建后它的哈希值可能被改变。
在下面的代码中,对象MutableKey的键在创建时变量 i=10 j=20,哈希值是1291。
然后我们改变实例的变量值,该对象的键 i 和 j 从10和20分别改变成30和40。现在Key的哈希值已经变成1931。
显然,这个对象的键在创建后发生了改变。所以类MutableKey是可变的。
public class MutableKey { private int i; private int j; public MutableKey(int i, int j) { this.i = i; this.j = j; } public final int getI() { return i; } public final void setI(int i) { this.i = i; } public final int getJ() { return j; } public final void setJ(int j) { this.j = j; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + i; result = prime * result + j; return result; } @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (obj == null) { return false; } if (!(obj instanceof MutableKey)) { return false; } MutableKey other = (MutableKey) obj; if (i != other.i) { return false; } if (j != other.j) { return false; } return true; }}public class MutableDemo { public static void main(String[] args) { // Object created MutableKey key = new MutableKey(10, 20); System.out.println("Hash code: " + key.hashCode()); // Object State is changed after object creation. key.setI(30); key.setJ(40); System.out.println("Hash code: " + key.hashCode()); }}
2、HashMap如何存储键值对
HashMap用Key的哈希值来存储和查找键值对。
当插入一个Entry时,HashMap会计算Entry Key的哈希值。Map会根据这个哈希值把Entry插入到相应的位置。
查找时,HashMap通过计算Key的哈希值到特定位置查找这个Entry。
所以,可变对象的hashCode会变化,变化之后可能在HashMap中找不到了
自定义类是不能当作Key的。
HashMap中的负载因子
在HashMap的构造函数有以下三种:
- HashMap():构建一个初始容量为 16,负载因子默认为 0.75 的 HashMap。
- HashMap(int initialCapacity):构建一个初始容量为 initialCapacity,负载因子为 0.75 的HashMap。
- HashMap(int initialCapacity, float loadFactor):以指定初始容量、指定的负载因子创建一个HashMap。
而其中的负载因子loadFactor的理解为:HashMap中的数据量/HashMap的总容量(initialCapacity),当loadFactor达到指定值或者0.75时候,HashMap的总容量自动扩展一倍(调用resize),以此类推。
负载因子是为了让查询效率更高,因为在数目过多了时候有可能冲突概率太大,这样很多时候是要遍历链表的。
HashMap中的resize
- Java 8 中实现
首先,size超过阈值会调resize函数,resize函数中新建一个散列表数组,容量为旧表的2倍,接着需要把旧表的键值对迁移到新表,这里分三种情况: - 表项只有一个键值对时,针对新表计算新的索引位置并插入键值对
- 表项节点是红黑树节点时(说明这个bin元素较多已经转成红黑树了),split这个bin。
- 表项节点包含多个键值对组成的链表时(拉链法)。jdk6和7的迁移实现(transfer函数)都是直接算新的索引位置然后头插法往拉链里填坑。
第3种情形的策略:
把链表上的键值对按hash值分成lo和hi两串,lo串的新索引位置与原先相同[原先位置j],hi串的新索引位置为[原先位置j+oldCap];
链表的键值对加入lo还是hi串取决于 判断条件if ((e.hash & oldCap) == 0),因为capacity是2的幂,所以oldCap为10…0的二进制形式,若判断条件为真,意味着oldCap为1的那位对应的hash位为0,对新索引的计算没有影响(新索引=hash&(newCap-1),newCap=oldCap<<2);若判断条件为假,则 oldCap为1的那位对应的hash位为1,
即新索引=hash&( newCap-1 )= hash&( (oldCap<<2) - 1),相当于多了10…0,即oldCap。
else { // preserve order Node<K,V> loHead = null, loTail = null; Node<K,V> hiHead = null, hiTail = null; Node<K,V> next; do { next = e.next; //对于16的大小数组,110000类是影响索引的,100000类不影响索引,索引的计算是对16取余数得来的,所以我们需要关注对32、16取余数不同的hashcode,也就是万位为1的 if ((e.hash & oldCap) == 0) { //不影响原有索引,00000,还在原有table[index]中 if (loTail == null) loHead = e; else loTail.next = e; loTail = e; } else { //影响原有索引,10000,应该在table[index+oldCap]中 if (hiTail == null) hiHead = e; else hiTail.next = e; hiTail = e; } } while ((e = next) != null); if (loTail != null) { loTail.next = null; newTab[j] = loHead; } if (hiTail != null) { hiTail.next = null; //改变索引对应的数组下标 newTab[j + oldCap] = hiHead; }}
LinkedHashMap
为了保存HashMap中插入的顺序,将HashMap中的Entry作为双向链表串联起来,引入Entry中的Entry before,Entry after:
static class Entry<K,V> extends HashMap.Node<K,V> { Entry<K,V> before, after; Entry(int hash, K key, V value, Node<K,V> next) { super(hash, key, value, next); }}
加入表头和表尾:
transient LinkedHashMap.Entry<K,V> head;/** * The tail (youngest) of the doubly linked list. */transient LinkedHashMap.Entry<K,V> tail;/** * The iteration ordering method for this linked hash map: <tt>true</tt> * for access-order, <tt>false</tt> for insertion-order. * * @serial */final boolean accessOrder;
accessOrder是标志是否将查询出来的元素放在最后一个,如果需要,应该这样创建:
LinkedHashMap<String, String> map = new LinkedHashMap<String, String>(16, 0.75f, true);
从迭代器可以看出before,after的作用:
- HashMap(查找当前table[index]的next,如果next==null,则继续查找table[index+1].head):
final Node<K,V> nextNode() { Node<K,V>[] t; Node<K,V> e = next; if (modCount != expectedModCount) throw new ConcurrentModificationException(); if (e == null) throw new NoSuchElementException(); if ((next = (current = e).next) == null && (t = table) != null) { do {} while (index < t.length && (next = t[index++]) == null); } return e;}
- LinkedHashMap(迭代的next一直是用after实现的):
final LinkedHashMap.Entry<K,V> nextNode() { LinkedHashMap.Entry<K,V> e = next; if (modCount != expectedModCount) throw new ConcurrentModificationException(); if (e == null) throw new NoSuchElementException(); current = e; next = e.after; return e;}
- Java HashMap整理
- Java-HashMap整理
- 【Java】HashMap、HashSet、TreeMap、TreeSet判断元素相同(代码整理)
- HashMap源码要点整理
- map hashmap整理
- HashMap工作原理整理
- hashmap源码学习整理
- java hashMap
- Java HashMap
- java---hashmap
- java hashmap
- Java HashMap
- JAVA-HASHMAP
- java Hashmap
- java HashMap
- Java HashMap
- java HashMap
- Java - HashMap
- Redis与Memcached的区别
- Shell基础(一):入门基础
- 124.View the Exhibit and examine the structure of the PROMOTIONS, SALES, and CUSTOMER tables.
- 微赞平台拼团插件的支付流程
- 2015AC前端大会笔记
- Java HashMap整理
- Android PopupWindow响应外部控件点击事件
- springmvc + ajaxfileupload 实现异步上传文件(图片)
- WebView的使用心得
- HDU 2190 悼念512汶川大地震遇难同胞——重建希望小学 (递推)
- swift学习-元组
- IOS -定位(一)
- JDBC - ResultSet
- 简单的使用jni调用java方法