开散列法又叫链地址法(开链法),首先对关键码集合用散列函数计算散列地址,具有相同地址的关键码归于同一子集合,每一个子集合称为一个桶,各个桶中的元素通过一个单链表链接起来,各链表的头结点存储在哈希表中。
从上图可以看出,开散列中每个桶中放的都是发生哈希冲突的元素
public class HashBuck测试1{ public static class Node { public T key; public V value; public Node next; public Node(T key, V value){ this.key = key; this.value = value; } } //负载因子 double DEFAULT_LOAD_FACTOR=0.75; //初始容量 int INIT_CAPACITY=8; public Node [] array = (Node []) new Node[INIT_CAPACITY]; public int usedSize; //利用反射创建泛型数组,但是在这里先用上面的形式 // public HashBuck(){ // array = (Node []) Array.newInstance(Node.class,INIT_CAPACITY); // } //插入数据 public void put(T key,V val){ Node node = new Node<>(key,val); int index=key.hashCode()% array.length; Node cur=array[index]; //查看是否已经存在key,如果存在就进行value的更新 while(cur!=null){ if(cur.key.equals(key)){ cur.value=val; return; } cur=cur.next; } node.next=array[index]; array[index]=node; usedSize++; //判断是否超过负载因子,如果超过就进行扩容和重写hash if(usedSize*1.0/ array.length>=DEFAULT_LOAD_FACTOR){ //进行扩容 Node []temp = new Node[array.length*2]; //重新Hash for(int i=0;i< array.length;i++){ cur=array[i]; while(cur!=null){ index=cur.key.hashCode()% array.length; Node nextCur=cur.next; cur.next=temp[index]; temp[index]=cur; cur=nextCur; } } array=temp; } } //删除数据 public void remove(T key){ int index=key.hashCode()%array.length; if(array[index].key.equals(key)){ array[index]=array[index].next; return; } Node cur=array[index].next; Node curPer=array[index]; while(cur!=null){ if(cur.key.equals(key)){ curPer.next=cur.next; return; } curPer=cur; cur=cur.next; } } }
public static void main(String[] args) {
HashBuck hashBuck = new HashBuck<>();
hashBuck.put("as",1);
hashBuck.put("sx",2);
hashBuck.put("fa",4);
hashBuck.put("da",3);
hashBuck.put("gz",5);
hashBuck.put("hx",6);
hashBuck.put("ja",7);
hashBuck.put("kx",8);
hashBuck.put("lw",9);
hashBuck.put("ma",10);
hashBuck.remove("gz");
hashBuck.remove("ma");
}
这里的测试属于是常规测试
测试2class Students {
int id;
String name;
public Students(int id, String name) {
this.id = id;
this.name = name;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Students students = (Students) o;
return id == students.id && name.equals(students.name);
}
@Override
public int hashCode() {
return Objects.hash(id, name);
}
}
public class TestHashBuck {
public static void main(String[] args) {
Students student1=new Students(123,"lisi");
Students student2=new Students(123,"lisi");
HashBuck hashBuck = new HashBuck();
hashBuck.put(student1,2);
hashBuck.put(student2,3);
}
}
在这个测试中,重写了 Students 类 的equals和hashcode方法
重写equals方法:使在id和name相同的情况下,判定为相同
重写hashcode方法:使id和name相同的情况下,形成相同的hash值
加入不进行上面的hashcode重写,就会出现下面的情况:
很明显,同一个学生生成了不一样的hashcode,这是不符合正常理解的,所以通过重写hashcode方法来满足自己的处理需求。
- 他们分别起什么作用?
hashcode确定下标的位置,而equals比较key是否相同
- 如果hashcode一样,那么equals一定一样吗?
不一定,因为同一个下标当中可能存储多个元素(链表)
- 如果equals一样,那么hashcode一定一样吗?
2. HashMap源码解析 基础规定一定,因为当equals一样时,说明两个对象相同,即hashcode一样。
在Java中,HashMap的底层是 数组+链表+红黑树,他们之间存在转换关系:
即有:当数组元素超过64并且链表长度超过8时,就进行树化
大小为0,当插入元素时,才会进行内容的分配,数组容量就变成了16.
面试问题:何时进行扩容当指定容量时,就会分配内存,大小为:大于指定容量,并且是最小的以2 为底的幂次方
当超过负载因子的时候进行扩容
后续还会进行补充~~



