JDK之Hash原理与算法
思考一下如何取得这几个数
显然,用0,1判断是最快的 没有值都是0,只有有值得才是1,要【判断是否这个数在这个数组里面就直接用if(50)=1即可知道了,够简单吧】,在这100个New 出来的数组空间中,显然这种方式不需要什么二分也不需要快速查找了,
但是有一个很重要缺点就是如果是一亿大小的话,这根本不可能New 一亿出来,不过这就是最简单原始的hash即散列表,就是在数组的基础上的散列。
而同样在散列表的基础上的函数就是散列函数(即)-------------------------------hash函数
让我们再来思考思考
刚刚的假设用到这里来,真的就一亿大小数组,怎样判断!
思考一办法1:取余
不能用0 1页很简单就是重复会很多。】、
所以还是要10
但是看下面这种方法还是有问题
a[0]冲突了即-----------------哈希冲突,
怎么办?所以这个方法不行!
不过讲到哈希冲突了那就可以了解一下它,会发现踏实不可避免的,而且经常用的java的md5就也是理论上的会哈希冲突
看图
那么肯定有问题就会有解决方案
方案一(不是重点).开放寻址
这样查找也很简单 ,比如要找20,从绿色的开始找,发现a[0]不仅是绿色的也是模之后值为0即a[0]=0,但是发现不是要的20所以下一位绿色,就这样找到a[2]
但是这样又有问题了那就是删除了数据怎么办,比如1没了即a[1]这时a[2]的20按照逻辑应该在a[1]去,才符合开放寻址的逻辑,这时候怎么办,Hash不能移!
看图
引进一个delete即删除了都会有delete标签,就像这个绿色标签一样,查找的时候所以就是查找delete,和绿色 两项不只是绿色。
方法二:链表(重点)
这时候的a[]就不存数据,而是key。如%10=0的即模10为0的都在 0这里找。链表的好处,详情参考数据结构链表 ,增加和插入删除等,很容易。不过! ------遍历比较慢。
这时就引进树
而为了不让查询时树也变成链表。所以引进红黑树,颜色,左右旋等操作。
那么我们的hashmap就是这样一个结构表+红黑树
首先得知道hashmap是有两个版本的jdk 1.7和1.8。而大于8的时候就引进了红黑树,7之前都是链表,为什么呢 因为Jdk经过测试,hashmap因为左右旋等操作会有消耗
在1.7里面反而更慢。在1.8才更快、看图
那么接来下开始hashmap的常见操作
ashMap共有4个构造函数,如下:
// 默认构造函数。 HashMap() // 指定“容量大小”的构造函数 HashMap(int capacity) // 指定“容量大小”和“加载因子”的构造函数 HashMap(int capacity, float loadFactor) // 包含“子Map”的构造函数 HashMap(Map<? extends K, ? extends V> map)
HashMap的API
void clear() //clear() 的作用是清空HashMap。它是通过将所有的元素设为null来实现的。 Object clone() //clone()方法的作用很简单,就是克隆一个HashMap对象并返回。 boolean containsKey(Object key) //containsKey() 的作用是判断HashMap是否包含key。返回“键为key”的键值对 boolean containsValue(Object value) //containsValue() 的作用是判断HashMap是否包含“值为value”的元素。第一,若“value为null”,则调用containsNullValue()。第二,若“value不为null”,则查找HashMap中是否有值为value的节点。 Set<Entry<K, V>> entrySet() //entrySet()的作用是返回“HashMap中所有Entry的集合”,它是一个集合。我们通过entrySet()获取到的Iterator的next()方法去遍历HashMap时,实际上调用的是 nextEntry() 。而nextEntry()的实现方式,先遍历Entry(根据Entry在table中的序号,从小到大的遍历);然后对每个Entry(即每个单向链表),逐个遍历。 V get(Object key) //get() 的作用是获取key对应的value boolean isEmpty() //判断是否为0 Set<K> keySet() //设置key V put(K key, V value) //put() 的作用是对外提供接口,让HashMap对象可以通过put()将“key-value”添加到HashMap中。 void putAll(Map<? extends K, ? extends V> map) //putAll() 的作用是将"map"的全部元素都添加到HashMap中 V remove(Object key) //remove() 的作用是删除“键为key”元素 int size() // Collection<V> values()
从图中可以看出:
(01) HashMap继承于AbstractMap类,实现了Map接口。Map是"key-value键值对"接口,AbstractMap实现了"键值对"的通用函数接口。
(02) HashMap是通过"拉链法"实现的哈希表。它包括几个重要的成员变量:table, size, threshold, loadFactor, modCount。
table是一个Entry[]数组类型,而Entry实际上就是一个单向链表。哈希表的"key-value键值对"都是存储在Entry数组中的。
size是HashMap的大小,它是HashMap保存的键值对的数量。
threshold是HashMap的阈值,用于判断是否需要调整HashMap的容量。threshold的值="容量*加载因子",当HashMap中存储数据的数量达到threshold时,就需要将HashMap的容量加倍。
loadFactor就是加载因子。
modCount是用来实现fail-fast机制的。
HashMap遍历方式
4.1 遍历HashMap的键值对
第一步:根据entrySet()获取HashMap的“键值对”的Set集合。
第二步:通过Iterator迭代器遍历“第一步”得到的集合。
// 假设map是HashMap对象 // map中的key是String类型,value是Integer类型 Integer integ = null; Iterator iter = map.entrySet().iterator(); while(iter.hasNext()) { Map.Entry entry = (Map.Entry)iter.next(); // 获取key key = (String)entry.getKey(); // 获取value integ = (Integer)entry.getValue(); }
4.2 遍历HashMap的键
第一步:根据keySet()获取HashMap的“键”的Set集合。
第二步:通过Iterator迭代器遍历“第一步”得到的集合。
// 假设map是HashMap对象 // map中的key是String类型,value是Integer类型 String key = null; Integer integ = null; Iterator iter = map.keySet().iterator(); while (iter.hasNext()) { // 获取key key = (String)iter.next(); // 根据key,获取value integ = (Integer)map.get(key); }
4.3 遍历HashMap的值
第一步:根据value()获取HashMap的“值”的集合。
第二步:通过Iterator迭代器遍历“第一步”得到的集合。
// 假设map是HashMap对象 // map中的key是String类型,value是Integer类型 Integer value = null; Collection c = map.values(); Iterator iter= c.iterator(); while (iter.hasNext()) { value = (Integer)iter.next(); }
下面通过一个实例学习如何使用HashMap
import java.util.Map;
import java.util.Random;
import java.util.Iterator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map.Entry;
import java.util.Collection;
/*
* @desc HashMap测试程序
*
* @author skywang
*/
public class HashMapTest {
public static void main(String[] args) {
testHashMapAPIs();
}
private static void testHashMapAPIs() {
// 初始化随机种子
Random r = new Random();
// 新建HashMap
HashMap map = new HashMap();
// 添加操作
map.put("one", r.nextInt(10));
map.put("two", r.nextInt(10));
map.put("three", r.nextInt(10));
// 打印出map
System.out.println("map:"+map );
// 通过Iterator遍历key-value
Iterator iter = map.entrySet().iterator();
while(iter.hasNext()) {
Map.Entry entry = (Map.Entry)iter.next();
System.out.println("next : "+ entry.getKey() +" - "+entry.getValue());
}
// HashMap的键值对个数
System.out.println("size:"+map.size());
// containsKey(Object key) :是否包含键key
System.out.println("contains key two : "+map.containsKey("two"));
System.out.println("contains key five : "+map.containsKey("five"));
// containsValue(Object value) :是否包含值value
System.out.println("contains value 0 : "+map.containsValue(new Integer(0)));
// remove(Object key) : 删除键key对应的键值对
map.remove("three");
System.out.println("map:"+map );
// clear() : 清空HashMap
map.clear();
// isEmpty() : HashMap是否为空
System.out.println((map.isEmpty()?"map is empty":"map is not empty") );
}
}
1 import java.util.Map; 2 import java.util.Random; 3 import java.util.Iterator; 4 import java.util.HashMap; 5 import java.util.HashSet; 6 import java.util.Map.Entry; 7 import java.util.Collection; 8 9 /* 10 * @desc HashMap测试程序 11 * 12 * @author skywang 13 */ 14 public class HashMapTest { 15 16 public static void main(String[] args) { 17 testHashMapAPIs(); 18 } 19 20 private static void testHashMapAPIs() { 21 // 初始化随机种子 22 Random r = new Random(); 23 // 新建HashMap 24 HashMap map = new HashMap(); 25 // 添加操作 26 map.put("one", r.nextInt(10)); 27 map.put("two", r.nextInt(10)); 28 map.put("three", r.nextInt(10)); 29 30 // 打印出map 31 System.out.println("map:"+map ); 32 33 // 通过Iterator遍历key-value 34 Iterator iter = map.entrySet().iterator(); 35 while(iter.hasNext()) { 36 Map.Entry entry = (Map.Entry)iter.next(); 37 System.out.println("next : "+ entry.getKey() +" - "+entry.getValue()); 38 } 39 40 // HashMap的键值对个数 41 System.out.println("size:"+map.size()); 42 43 // containsKey(Object key) :是否包含键key 44 System.out.println("contains key two : "+map.containsKey("two")); 45 System.out.println("contains key five : "+map.containsKey("five")); 46 47 // containsValue(Object value) :是否包含值value 48 System.out.println("contains value 0 : "+map.containsValue(new Integer(0))); 49 50 // remove(Object key) : 删除键key对应的键值对 51 map.remove("three"); 52 53 System.out.println("map:"+map ); 54 55 // clear() : 清空HashMap 56 map.clear(); 57 58 // isEmpty() : HashMap是否为空 59 System.out.println((map.isEmpty()?"map is empty":"map is not empty") ); 60 } 61 }
(某一次)运行结果:
map:{two=7, one=9, three=6} next : two - 7 next : one - 9 next : three - 6 size:3 contains key two : true contains key five : false contains value 0 : false map:{two=7, one=9} map is empty