java集合-HashMap(JDK1.8)
来源:互联网 发布:信誉出肉 淘宝 编辑:程序博客网 时间:2024/06/07 03:09
一、基本概念
HashMap基于哈希表的 Map 接口的实现。此实现提供所有可选的映射操作,并允许使用 null 值和 null 键。以前JDK中HashMap采用的是位桶+链表的方式,即我们常说的散列链表的方式,而JDK1.8中采用的是位桶+链表/红黑树的方式,也是非线程安全的。当某个位桶的链表的长度达到某个阀值的时候,这个链表就将转换成红黑树。
注意事项:
- HashMap 是一个散列表,它存储的内容是键值对(key-value)映射;
- HashMap 继承于AbstractMap,实现了Map、Cloneable、java.io.Serializable接口;
- HashMap 的实现不是同步的,这意味着它不是线程安全的。它的key、value都可以为null;
- HashMap中的映射不是有序的;
- HashMap 的实例有两个参数影响其性能:“初始容量” 和 “加载因子”;
二、源码分析
1:常量
/** map的最大容量 */ static final int MAXIMUM_CAPACITY = 1 << 30; /** * 默认加载因子 */ static final float DEFAULT_LOAD_FACTOR = 0.75f; /** 将list链表转为红黑数阀值,即list的size超过时转化 */ static final int TREEIFY_THRESHOLD = 8; /** resize操作中,决定是否untreeify的阈值 */ static final int UNTREEIFY_THRESHOLD = 6; /** 决定是否转换成tree的最小容量 */ static final int MIN_TREEIFY_CAPACITY = 64;
2:主要字段
/**存储元素的数组*/ transient Node<K,V>[] table; /** 用于map迭代遍历 */ transient Set<Map.Entry<K,V>> entrySet; /** 元素个数 */ transient int size; /** 修改次数 */ transient int modCount; /** 阀值,用于扩容阀值 */ int threshold; /** 加载因子 */ final float loadFactor;
3:主要方法
get方法:
public V get(Object key) { Node<K,V> e; return (e = getNode(hash(key), key)) == null ? null : e.value; } /** * Implements Map.get and related methods * * @param hash hash for key * @param key the key * @return the node, or null if none */ final Node<K,V> getNode(int hash, Object key) { Node<K,V>[] tab; Node<K,V> first, e; int n; K k; if ((tab = table) != null && (n = tab.length) > 0 && (first = tab[(n - 1) & hash]) != null) { if (first.hash == hash && // 总是判断第一个元素是否满足条件 ((k = first.key) == key || (key != null && key.equals(k)))) return first; if ((e = first.next) != null) { //第一个节点为TreeNode,则调用TreeNode.getTreeNode()方法遍历红黑数进行查询 if (first instanceof TreeNode) return ((TreeNode<K,V>)first).getTreeNode(hash, key); do { if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k)))) return e; } while ((e = e.next) != null); } } return null; }
put方法
public V put(K key, V value) { return putVal(hash(key), key, value, false, true); } /** * Implements Map.put and related methods * * @param hash hash for key * @param key the key * @param value the value to put * @param onlyIfAbsent if true, don't change existing value * @param evict if false, the table is in creation mode. * @return previous value, or null if none */ final V putVal(int hash, K key, V value, boolean onlyIfAbsent, boolean evict) { Node<K,V>[] tab; Node<K,V> p; int n, i; if ((tab = table) == null || (n = tab.length) == 0) //table为空,n为table的长度 n = (tab = resize()).length; //i位置为空,直接存储 if ((p = tab[i = (n - 1) & hash]) == null) tab[i] = newNode(hash, key, value, null); else { // 若i位置上的值不为空,判断当前位置上的Node p 是否与要插入的key的hash和key相同 Node<K,V> e; K k; if (p.hash == hash && ((k = p.key) == key || (key != null && key.equals(k)))) //相同key时直接覆盖 e = p; else if (p instanceof TreeNode) //不相同时,若当前p已经为TreeNode,则插入该树上 e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value); else { //在i位置上的链表中找到p.next为null的位置,binCount计算出当前链表的长度,如果继续将冲突的节点插入到该链表中,会使链表的长度大于tree化的阈值,则将链表转换成tree。 for (int binCount = 0; ; ++binCount) { if ((e = p.next) == null) { p.next = newNode(hash, key, value, null); if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st treeifyBin(tab, hash); break; } if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k)))) break; p = e; } } if (e != null) { // existing mapping for key V oldValue = e.value; if (!onlyIfAbsent || oldValue == null) e.value = value; afterNodeAccess(e); return oldValue; } } ++modCount; if (++size > threshold) resize(); afterNodeInsertion(evict); return null; }
resize(),由于解决冲突的方法可能是list,也可能是红黑数,所以resize()较为复杂点。
/** * Initializes or doubles table size. If null, allocates in * accord with initial capacity target held in field threshold. * Otherwise, because we are using power-of-two expansion, the * elements from each bin must either stay at same index, or move * with a power of two offset in the new table. * * @return the table */ final Node<K,V>[] resize() { Node<K,V>[] oldTab = table; int oldCap = (oldTab == null) ? 0 : oldTab.length; int oldThr = threshold; int newCap, newThr = 0; if (oldCap > 0) { //若超过最大容量,则不能扩容 if (oldCap >= MAXIMUM_CAPACITY) { threshold = Integer.MAX_VALUE; return oldTab; } else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY && oldCap >= DEFAULT_INITIAL_CAPACITY) newThr = oldThr << 1; //阀值扩大2倍 } else if (oldThr > 0) // initial capacity was placed in threshold newCap = oldThr; else { // zero initial threshold signifies using defaults newCap = DEFAULT_INITIAL_CAPACITY; newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY); } if (newThr == 0) { float ft = (float)newCap * loadFactor; newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ? (int)ft : Integer.MAX_VALUE); } threshold = newThr; @SuppressWarnings({"rawtypes","unchecked"}) // 创建容量为newCap的newTab,并将oldTab中的Node迁移过来,这里需要考虑链表和tree两种情况。 Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap]; table = newTab; if (oldTab != null) { for (int j = 0; j < oldCap; ++j) { Node<K,V> e; if ((e = oldTab[j]) != null) { oldTab[j] = null; if (e.next == null) newTab[e.hash & (newCap - 1)] = e; else if (e instanceof TreeNode) // split方法会将树分割为lower 和upper tree两个树,如果子树的节点数小于了UNTREEIFY_THRESHOLD阈值,则将树untreeify,将节点都存放在newTab中。 ((TreeNode<K,V>)e).split(this, newTab, j, 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; if ((e.hash & oldCap) == 0) { if (loTail == null) loHead = e; else loTail.next = e; loTail = e; } else { 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; } } } } } return newTab; }
remove()方法
public V remove(Object key) { Node<K,V> e; return (e = removeNode(hash(key), key, null, false, true)) == null ? null : e.value; } /** * Implements Map.remove and related methods * * @param hash hash for key * @param key the key * @param value the value to match if matchValue, else ignored * @param matchValue if true only remove if value is equal * @param movable if false do not move other nodes while removing * @return the node, or null if none */ final Node<K,V> removeNode(int hash, Object key, Object value, boolean matchValue, boolean movable) { Node<K,V>[] tab; Node<K,V> p; int n, index; if ((tab = table) != null && (n = tab.length) > 0 && (p = tab[index = (n - 1) & hash]) != null) { Node<K,V> node = null, e; K k; V v; if (p.hash == hash && ((k = p.key) == key || (key != null && key.equals(k)))) node = p; //若是用红黑数解决冲突则getTreeNode方法查找到节点 else if ((e = p.next) != null) { if (p instanceof TreeNode) node = ((TreeNode<K,V>)p).getTreeNode(hash, key); else { do { if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k)))) { node = e; break; } p = e; } while ((e = e.next) != null); } } if (node != null && (!matchValue || (v = node.value) == value || (value != null && value.equals(v)))) { if (node instanceof TreeNode) ((TreeNode<K,V>)node).removeTreeNode(this, tab, movable); else if (node == p) tab[index] = node.next; else p.next = node.next; ++modCount; --size; afterNodeRemoval(node); return node; } } return null; }
HashMap实例
1:Hashmap的遍历方法
package com.csu.collection;import java.util.HashMap;import java.util.Iterator;import java.util.Map.Entry;import java.util.Set;public class HashMapTest { public static void main(String[]args) { HashMap<Integer, Integer> map=new HashMap<>(); for(int i=0;i<10000000;i++) { map.put(i, i); } System.out.println("第一种遍历方法:for each map.entrySet()"); long startTime1=System.currentTimeMillis(); for(Entry<Integer, Integer> entry:map.entrySet()) { entry.getValue(); entry.getKey(); } long endTime1=System.currentTimeMillis(); System.out.println("第一种遍历方法用时:"+(endTime1-startTime1)+"ms"); System.out.println("第2种遍历方法:map.entrySet()的集合迭代器"); long startTime2=System.currentTimeMillis(); Iterator<Entry<Integer, Integer>> iterator=map.entrySet().iterator(); while(iterator.hasNext()) { HashMap.Entry<Integer, Integer> entry=(Entry<Integer, Integer>) iterator.next(); entry.getValue(); entry.getKey(); } long endTime2=System.currentTimeMillis(); System.out.println("第2种遍历方法用时:"+(endTime2-startTime2)+"ms"); System.out.println("第3种遍历方法: for each map.keySet(),再调用get获取"); long startTime3=System.currentTimeMillis(); for (Integer key : map.keySet()) { map.get(key); } long endTime3=System.currentTimeMillis(); System.out.println("第3种遍历方法用时:"+(endTime3-startTime3)+"ms"); System.out.println("第4种遍历方法:for each map.entrySet(),用临时变量保存map.entrySet()"); long startTime4=System.currentTimeMillis(); Set<Entry<Integer, Integer>> entrySet = map.entrySet(); for (Entry<Integer, Integer> entry : entrySet) { entry.getKey(); entry.getValue(); } long endTime4=System.currentTimeMillis(); System.out.println("第4种遍历方法用时:"+(endTime4-startTime4)+"ms"); }}
运行结果
第一种遍历方法:for each map.entrySet()第一种遍历方法用时:71ms第2种遍历方法:map.entrySet()的集合迭代器第2种遍历方法用时:83ms第3种遍历方法: for each map.keySet(),再调用get获取第3种遍历方法用时:117ms第4种遍历方法:for each map.entrySet(),用临时变量保存map.entrySet()第4种遍历方法用时:84ms
总结:
- a. HashMap的循环,如果既需要key也需要value,直接用for each map.entrySet();
- 如果只是遍历key而无需value的话,可以直接用for each map.keySet(),再调用get获取。
2:使用Hashmap 实现缓存
public class Student { private String name; private String address; public Student(String name,String address) { this.address=address; this.name=name; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getAddress() { return address; } public void setAddress(String address) { this.address = address; }}
import java.io.Serializable;public class CacheEntity implements Serializable { /** * */ private static final long serialVersionUID = 1L; private final int DEFUALT_TIME=200;//秒 private String key; private Object value; private int time;//缓存存活时间,不设置则使用默认值 private long timeoutStamp;// 缓存过期时间戳 @SuppressWarnings("unused") private CacheEntity() { this.timeoutStamp=System.currentTimeMillis()+DEFUALT_TIME*1000; this.time=DEFUALT_TIME; } public CacheEntity(String key,Object value) { this.key=key; this.value=value; } public CacheEntity(String key,Object value,long timestamp) { this(key,value); this.timeoutStamp=timestamp; } public CacheEntity(String key,Object value,int time) { this(key,value); this.time=time; this.timeoutStamp=System.currentTimeMillis()+DEFUALT_TIME*1000; } public String getKey() { return key; } public void setKey(String key) { this.key = key; } public Object getValue() { return value; } public void setValue(Object value) { this.value = value; } public int getTime() { return time; } public void setTime(int time) { this.time = time; } public long getTimeoutStamp() { return timeoutStamp; } public void setTimeoutStamp(long timeoutStamp) { this.timeoutStamp = timeoutStamp; }}
import java.util.ArrayList;import java.util.HashMap;import java.util.List;/** * * 采用队列,定时循环清理过期缓存 * */public class CacheByHashMap { private static HashMap<String, CacheEntity> map; private static List<CacheEntity> tempList; static{ tempList=new ArrayList<CacheEntity>(); map=new HashMap<String,CacheEntity>(1<<10); new Thread(new RemoveTimeOutCacheThread()).start(); } /** * 添加缓存 * @param key * @param value * @param time */ public static synchronized void addCache(String key,CacheEntity value,int time) { value.setTimeoutStamp(System.currentTimeMillis()+time*1000); map.put(key, value); tempList.add(value); } /** * 获取缓存对象 * @param key * @return */ public static synchronized CacheEntity getCache(String key) { return map.get(key); } /** * 检查是否包含特定的key * @param key * @return */ public static synchronized boolean isContainsKey(String key) { return map.containsKey(key); } /** * 删除缓存 * @param key */ public static synchronized void removeCache(String key) { map.remove(key); } /** * 获取缓存数量 * @return */ public static int getCacheSize() { return map.size(); } /** * 清除所有缓存 */ public static synchronized void clearCache() { tempList.clear(); map.clear(); System.out.println("所有缓存被清理"); } static class RemoveTimeOutCacheThread implements Runnable{ @Override public void run() { // TODO Auto-generated method stub while(true) { try { checkTime(); } catch (Exception e) { // TODO: handle exception e.printStackTrace(); } } } private void checkTime() throws InterruptedException { CacheEntity value=null; long timeoutTime=1000l; if(tempList.size()<1) { System.out.println("过期队列为空!"); timeoutTime=1000l; Thread.sleep(timeoutTime); return ; } value=tempList.get(0); timeoutTime=value.getTimeoutStamp()-System.currentTimeMillis(); if(timeoutTime>0) { Thread.sleep(timeoutTime); return ; } System.out.println("清除过期缓存"+value.getKey()); tempList.remove(value); removeCache(value.getKey()); } }}
测试端代码:
public class CacheTest { public static void main(String[] args) { // TODO Auto-generated method stub Student student1=new Student("zhangsan", "shangsha"); Student student2=new Student("wangqiang", "beijing"); Student student3=new Student("zhangsi", "shanghai"); Student student4=new Student("zhangwu", "wuhan"); Student student5=new Student("zhangqi", "zhengzhou"); Student student6=new Student("zhangba", "shangsha"); CacheEntity cacheEntity1=new CacheEntity("1", student1, 30); CacheEntity cacheEntity2=new CacheEntity("2", student2, 30); CacheEntity cacheEntity3=new CacheEntity("3", student3, 30); CacheEntity cacheEntity4=new CacheEntity("4", student4, 30); CacheEntity cacheEntity5=new CacheEntity("5", student5, 30); CacheEntity cacheEntity6=new CacheEntity("6", student6, 30); //添加缓存 CacheByHashMap.addCache(cacheEntity1.getKey(), cacheEntity1, cacheEntity1.getTime()); CacheByHashMap.addCache(cacheEntity2.getKey(), cacheEntity2, cacheEntity2.getTime()); CacheByHashMap.addCache(cacheEntity3.getKey(), cacheEntity3, cacheEntity3.getTime()); CacheByHashMap.addCache(cacheEntity4.getKey(), cacheEntity4, cacheEntity4.getTime()); CacheByHashMap.addCache(cacheEntity5.getKey(), cacheEntity5, cacheEntity5.getTime()); CacheByHashMap.addCache(cacheEntity6.getKey(), cacheEntity6, cacheEntity6.getTime()); if(CacheByHashMap.isContainsKey("2")) { System.out.println(" 该对象已有缓存"); //这里就可以获取缓存如get() } else { CacheByHashMap.addCache(cacheEntity2.getKey(), cacheEntity2, cacheEntity2.getTime()); //这里可以模拟从数据库获取数据,添加到缓存 } }}
运行结果:
该对象已有缓存清除过期缓存1清除过期缓存2清除过期缓存3清除过期缓存4清除过期缓存5清除过期缓存6过期队列为空!过期队列为空!
1 0
- java集合-HashMap(JDK1.8)
- Java集合学习之HashMap 一(JDK1.8)
- java集合(4):HashMap源码分析(jdk1.8)
- Java集合类框架学习 4.3 —— HashMap(JDK1.8)
- java基础提高篇--集合源码分析--jdk1.8 HashMap源码
- java集合框架中HashMap源码(基于JDK1.6)
- Java集合框架--HashMap源码解析(JDK1.7)
- JDK1.8源码学习之 HashMap.java
- Java Jdk1.8 HashMap源码阅读
- Java源码分析之HashMap(JDK1.8)
- 深入理解Java HashMap(JDK1.8)
- Java源码分析之HashMap(JDK1.8)
- 【Java】HashMap源码分析(JDK1.8)
- jdk1.8 hashmap
- 数据结构--jdk1.8 HashMap
- HashMap(JDK1.8)
- HashMap(jdk1.8)
- JDK1.8 HashMap
- iOS 检测字符串输入是否为合法数字
- linux shell基本命令
- Android View事件的分发机制
- mac或者linux下adb连接不上
- Spring事务配置的五种方式
- java集合-HashMap(JDK1.8)
- App架构设计经验之谈
- 一道关于图种的题
- ffmpeg实现H.264视频解码-1
- PTA03-树3 Tree Traversals Again
- AngularJS 学习笔记 -- 指令(Directive)
- 机器学习实战开始啦!
- Spring 学习时遇到错误整理贴(会慢慢补充)
- Win10远程时,提示“您的凭证不工作”解决办法