Hash 底层
What is hashMap?
Array and LinkedList的一次重要的结合, HashMap
hash一个字符串,hash任意一个object会得到一个数字(hashCode)
Map的底层本质:Array of LinkedList
What is hash function?
hash function
input: object
output: number
good hash function
不存在放进两个不同的object得到同一个number
How to implement it?
A table of buckets(an array of buckets), using the array index to denote each bucket
for each <key, value>, it goes to one of the buckets, the bucket index is determined by a hash function applied to the key and the size of the array
Hash的步骤
将输入的元素转换为一串唯一的整数值
将整数值对童的数量取模将该整数值转换为桶的index,即index = x mod n
将元素储存在与该index对应的桶中
Collision Control
当两个元素使用同一个hash函数映射到同一个位置(index)时,就会发生冲突。
为了解决这个问题,需要使用collision control技术
比如说seperate chaining(链式储存)和open addressing(开放地址探测)
Separate Chaining
如果发生碰撞,元素将被添加到桶中的链表中
这个链表时按照添加顺序添加和维护的,因此对于LinkedList的操作比对于HashSet的操作押送满
在极少数的情况下,所有的元素都被散列在到同一个桶里,这将导致性能下降到O(n)的级别
Open Addressing
当发生碰撞时,元素将尝试在桶中查询下一个可用的bucket。有三种重要的方法:
注意,放进去用的哪种方法,查找的时候也要用同一种方法
线性探测(Linear Probing): 在发生碰撞时,元素将尝试在桶的下一个位置查找可用的bucket。如果下一个位置也被占用了,它会继续寻找下一个,直到找到空的bucket
二次探测(Quadratic Probing):在发生碰撞时,元素将尝试在桶中查找可用的bucket。如果该位置被占用,则它将尝试在桶中,查找下一个位置,该位置时原始哈希位置加上1的平方。如果该位置被占用,则寻找下一个
双重散列(Double Hashing):在发生碰撞的时候,元素将尝试在桶中查找可用的buckted。他将使用另一个哈希函数来确定下一个位置的偏移量。这个过程将继续下去,直到一个空的buckted被找到为止
Rehashing, Load factor, Capacity
Rehashing: 当HashMap中的元素数量超过一定Load factor(负载因子)时,HashMap需要扩容。
这涉及到创建一个新的桶数组,并将所有元素从旧数组rehash到新数组中。
这操作可能会很昂贵,因此选择一个良好的负载因子(load factor)来最小调整大小的次数非常重要
Load factor(负载因子):负载因子确定HashMap需要调整大小的点。
较高的负载因子意味着更少的调整大小,但也意味着更多的碰撞和更长的桶链。
较低的负载因子意味着更多的调整大小,但也意味着更少的碰撞和更短的桶链。
Java默认的Load factor值时0.75(e.g. map里的entry数/桶数 《= 0.75)
Capacity(容量):HashMap的容量时它可以容纳的桶数。选择足够大的容量以避免频繁调整大小非常重要,但是也不要太大以免浪费内存
==, equals(), hashcode()
==
确定两个primitive types是否具有相同的值
确定两个references是否指向相同的jobjects
equals(), hashcode()
定在Object类中,Object是java类的根类
任何java类都隐式地扩展Object类,因此如果在子类里面没有重写这两个方法,他就会继承Object类中的定义的内容
equals()默认实现是检查两个referecne是否指向相同的对象"==",即内容是否相等
Java中的==和equals,前者比较两个Objects的reference是否相等,后者比较两个objects的内容是否相等,而hashcode方法则用来计算key的hash值,进而决定这个key应该落在哪个桶里面。
因此,在实现自定的Object为key时,需要同时重写equals和hashcode方法,否则会2出现相同的key被多次添加到集合中的情况,以保证hashset和hashmap的正确使用。
import java.util.HashSet;
import java.util.Set;
class Person {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (!(obj instanceof Person)) return false;
Person person = (Person) obj;
if (this.age != person.age) return false;
return (name != null ? name.equals(person.name) : null) && (this.age == person.age);
}
@Override
public int hashCode() {
return 31* name.hashCode() + 31 * 31 * age;
}
}
public class Main {
public static void main(String[] args) {
Person p1 = new Person("Harry", 18);
Person p2 = new Person("Harry", 18);
Set<Person> set = new HashSet<>();
set.add(p1);
set.add(p2);
}
}
Last updated