hashMap是我们常用的一种java集合类,在jdk1.8中以数组+链表+红黑树实现。使用put(key, value)存储对象到HashMap中,使用get(key)从HashMap中获取对象。当我们给put()方法传递键和值时,我们先对键调用hashCode()方法,返回的hashCode用于找到bucket(桶)位置来储存对象。
以下解释引自https://www.cnblogs.com/fengli9998/p/12092408.html
put存值的方法,过程如下:
①.判断键值对数组table[i]是否为空或为null,否则执行resize()进行扩容;
②.根据键值key计算hash值得到插入的数组索引i,如果table[i]==null,直接新建节点添加,转向⑥,如果table[i]不为空,转向③;
③.判断table[i]的首个元素是否和key一样,如果相同直接覆盖value,否则转向④,这里的相同指的是hashCode以及equals;
④.判断table[i] 是否为treeNode,即table[i] 是否是红黑树,如果是红黑树,则直接在树中插入键值对,否则转向⑤;
⑤.遍历table[i],判断链表长度是否大于8,大于8的话把链表转换为红黑树,在红黑树中执行插入操作,否则进行链表的插入操作;遍历过程中若发现key已经存在直接覆盖value即可;
⑥.插入成功后,判断实际存在的键值对数量size是否超多了最大容量threshold,如果超过,进行扩容。
get取值的方法,过程如下:
①.指定key 通过hash函数得到key的hash值
int hash=key.hashCode();
②.调用内部方法 getNode(),得到桶号(一般为hash值对桶数求模)
int index =hash%Entry[].length;
jdk1.6版本后使用位运算替代模运算,int index=hash&( Entry[].length - 1);
③.比较桶的内部元素是否与key相等,若都不相等,则没有找到。相等,则取出相等记录的value。
④.如果得到 key 所在的桶的头结点恰好是红黑树节点,就调用红黑树节点的 getTreeNode() 方法,否则就遍历链表节点。getTreeNode 方法使通过调用树形节点的 find()方法进行查找。由于之前添加时已经保证这个树是有序的,因此查找时基本就是折半查找,效率很高。
⑤.如果对比节点的哈希值和要查找的哈希值相等,就会判断 key 是否相等,相等就直接返回;不相等就从子树中递归查找。
本文用数组和链表的方式实现简单的put(key,value)、get(key)和remove(key),未实现hashMap达到扩容阀值是的resize()与相关红黑树的操作,请见谅
//数据域
class Data{
private int id;
private String name;
public Data(int id, String name) {
super();
this.id = id;
this.name = name;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Data [id=" + id + ", name=" + name + "]";
}
}
class Node{
private Integer key; //存入map的key
private Data data; //map中的value
public Node next; //下一节点
public Node(Integer key, Data data) {
super();
this.key = key;
this.data = data;
}
public Integer getKey() {
return key;
}
public void setKey(Integer key) {
this.key = key;
}
public Data getData() {
return data;
}
public void setData(Data data) {
this.data = data;
}
public Node getNext() {
return next;
}
public void setNext(Node next) {
this.next = next;
}
}
//管理多条链表
class HashMap{
private int initNum=8; //hashMap数组初始长度为8,真实的为16
private LinkedList[] mapList;
//初始化
public HashMap() {
mapList=new LinkedList[initNum];
//初始化每一条链表
for(int i=0;i<initNum;i++) {
mapList[i]=new LinkedList();
}
}
//添加课程
public void put(Integer key,Data data) {
//先用hash算法计算出,添加到的数组下标;
Node node=new Node(key,data);
int i=hash(key);
LinkedList list=mapList[i];
list.add(node,key);
}
public void get(Integer key) {
int i=hash(key);
LinkedList list=mapList[i];
list.get(key);
}
public void remove(Integer key) {
int i=hash(key);
LinkedList list=mapList[i];
list.remove(key);
}
//根据key的hash算法获取存储索引,尽可能实现均匀分布
public int hash(Integer key) {
return key.hashCode()%mapList.length;
}
public void show() {
for(int i=0;i<mapList.length;i++) {
LinkedList list=mapList[i];
list.show(i+1);
}
}
}
//创建单链表
class LinkedList{
private Node head;//第一个节点,默认为null
//以前插法
public void add(Node node,Integer key) {
//第一个节点把Node覆盖head
if(head==null) {
head=node;
return;
}
//key和首节点相同,覆盖
if(head.getKey().equals(key)){ //如果可以key相同,则覆盖值
head.setData(node.getData());
return;
}
//不相同则前插
Node temp=head;
head=node;
head.next=temp;
}
public void remove(Integer key) {
// TODO Auto-generated method stub
if(head==null) {
System.out.println("Map中没有key为"+key+"的值,无法删除");
return;
}
Node curNode=head;
//找到要删除的前一个
while(true) {
if(curNode.next==null||curNode.next.getKey().equals(key)) {
break;
}
curNode=curNode.next;
}
if(curNode.next!=null) {
curNode.next=curNode.next.next;
System.out.println("Map中key为"+key+"已被删除");
}else {
System.out.println("Map中没有key为"+key+"该值,无法删除");
}
}
//通过key获取value
public void get(Integer key) {
if(head==null) {
System.out.println("Map中没有key为"+key+"的值");
return;
}
Node curNode=head;
while(true) {
if(curNode==null||curNode.getKey().equals(key)) {
break;
}
curNode=curNode.next;
}
if(curNode!=null) {
System.out.println(curNode.getData().toString());
}else {
System.out.println("Map中没有key为"+key+"该值");
}
}
public void show(int num) {
if(head==null) {
System.out.println("第"+num+"条链表为空");
return;
}
Node curNode=head;
System.out.println("第"+num+"条链表数据:");
while(true) {
System.out.println("key:"+curNode.getKey()+" data:"+curNode.getData());
if(curNode.next==null) {
break;
}
curNode=curNode.next;
}
}
}
public class HashMapDemo {
public static void main(String[] args) {
// TODO Auto-generated method stub
//利用数组+链表的节点前插方式简单实现HashMap
//不涉及扩容与jdk8后中的链表长度为8时转化为红黑树
HashMap map=new HashMap();
map.put(1, new Data(1,"Java"));
map.put(2, new Data(2,"C++"));
map.put(3, new Data(3,"C"));
map.put(4, new Data(4,"PHP"));
map.put(8, new Data(5,"MySql"));
map.put(9, new Data(6,"SqlServer"));
map.put(10, new Data(7,"Spring"));
map.put(16, new Data(8,"SpringMvc"));
map.put(17, new Data(9,"数据结构"));
map.show();
System.out.println();
map.get(1);
map.get(18);
map.remove(9);
map.remove(20);
map.show();
}
}