Java 中的HashMap
哈希表是一个散列表,存储着Key-Value键值对,插入和查找的复杂度均为O(1)。
java中在创建哈希表时会创建一个默认大小的数组。插入一个键值对时,利用一个哈希算法确定key存储在数组的哪个位置,这时可能会出现哈希冲突问题,即不同key生成同样的hashcode。
此时利用链地址法,将每个位置创建一个链表来解决该问题,可将键值对插入到链表尾部,当链表长度大于8时转化为红黑树,当长度小与6时会将红黑树转化为链表。
在java中当实际负载因子大于默认的负载因子(0.75)时会触发扩容机制,每次扩容时容量变为原来大小的两倍
默认大小
默认的哈希表容量为16,当负载因子达到0.75时,某个链表长度为8的概率是非常小的。
哈希表的扩容机制
负载因子 = 存储的键值对元素个数/哈希表的容量
在java中当实际负载因子大于默认的负载因子(0.75)时会触发扩容机制,每次扩容时容量变为原来大小的两倍
当链表长度大于8时转化为红黑树,当长度小与6时会将红黑树转化为链表。因为链表的查询复杂度为O(n)而红黑树的查询复杂度为O(logn),在n较小时差距不大,当n变多后则红黑树的优势就体现了出来。一般来说hash算法使某个链表节点数变为8的概率非常之小,主要是为了防止使用了不合适的hash算法。
哈希算法
hash算法能将任意长度的二进制明文映射为较短的二进制串,并且不同的明文很难映射为相同的hash值
输入的数据可以有无穷个,而哈希值的取值范围有限,所以可能会出现哈希冲突,好的哈希算法会有效避免这种冲突
具有如下几个特点
1、正向快速
可以在有限时间资源内计算得到hash值
2、逆向困难
给定hash值,很难倒推明文
3、输入敏感
输入信息发生任何变化都会导致hash值出现很大变化
4、冲突避免
很难找到不同明文的hash值相同的情况
简单实现哈希表
public class HashMapTest {
//保存链表的数组
public Linked[] linkArr;
public static final int LEN = 16;
public HashMapTest(int len) {
//初始长度
linkArr = new Linked[len];
}
//默认长度
public HashMapTest() {
this(LEN);
}
//put数据
public void put(Object key,Object value) {
//根据key 值,计算数组(保存位置)
if(key == null) {
if(linkArr[0] == null) {
linkArr[0] = new Linked();
}
linkArr[0].add(key, value);
return;
}
int index = hash(key);
if(linkArr[index] == null) {
linkArr[index] = new Linked();
}
//找到该key对应的链表,添加到末尾
linkArr[index].add(key, value);
}
//get数据
public Object get(Object key){
int index;
//找到给key
if(key ==null) {
index = 0;
}else {
index = hash(key);
}
return linkArr[index].get(key);
}
//哈希算法
//系统中的哈希算法?
public int hash(Object key) {
return Math.abs(key.hashCode())%LEN;
}
public static void main(String[] args) {
HashMapTest test = new HashMapTest(16);
test.put("hisahd", 885);
test.put("hisahd", 777);
test.put(null, null);
System.out.println(test.get(null));
System.out.println(test.get("hisahd"));
}
}
自定义的链表类
public class Linked {
public Node root;
public Node last;
public int size = 0;
//初始化根节点
public Linked() {
root = new Node();
}
//添加节点到末尾
public void add(Object key,Object value) {
if(this.contain(key)==true) {
this.reset(key, value);
return;
}
Node point = new Node(key,value);
Node head = root.next;
if(head == null) {
root.next = point;
root.next.key = key;
root.next.value = value;
last = head;
}else {
last.next = point;
last = point;
last.key = key;
last.value = value;
}
++size;
}
public Object get(Object key) {
Node head = root.next;
if(key == null) {
for(int i = 0; i < size; ++i) {
if(head.key == null) {
return head.value;
}
head = head.next;
}
}else {
for(int i = 0; i < size; ++i) {
if(head.key.equals(key)) {
return head.value;
}
head = head.next;
}
}
return "不存在";
}
public boolean contain(Object key) {
Node head = root.next;
if(key == null) {
for(int i = 0; i < size; ++i) {
if(head.key == null) {
return true;
}
head = head.next;
}
}else {
for(int i = 0; i < size; ++i) {
if(head.key.equals(key)) {
return true;
}
head = head.next;
}
}
return false;
}
public void reset(Object key, Object value) {
Node head = root.next;
if(key == null) {
for(int i = 0; i < size; ++i) {
if(head.key == null) {
head.value = value;
}
head = head.next;
}
}else {
for(int i = 0; i < size; ++i) {
if(head.key.equals(key)) {
head.value = value;
}
head = head.next;
}
}
}
}
//节点类
class Node{
public Object key;
public Object value;
public Node next;
public Node() {
}
public Node(Object key,Object value) {
this.key = key;
this.value = value;
}
}