数据结构与算法-Hash算法-2
- [5 HashMap(又双叒叕提到红黑树动态扩容)](#5 HashMap(又双叒叕提到红黑树动态扩容))
- [6 如何设计Hash](#6 如何设计Hash)
- [7 Hash的应用](#7 Hash的应用)
- [8 HashMap源码(又双叒叕)](#8 HashMap源码(又双叒叕))
5 HashMap(又双叒叕提到红黑树动态扩容)
由于链表这种结构确实存在一些缺点,所以在我们的JDK中对之进行了优化,引入了更高效的数据结构:红黑树
-
初始大小:HashMap默认的初始大小是16,这个默认值是可以设置的,如果事先知道大概的数据量有多大,可以通过修改默认初始大小,减少动态扩容的次数,这样会大大提高HashMap的性能。
-
动态扩容:最大装载因子默认是0.75,当HashMap中元素个数超过0.75*capacity(capacity表示散列表的容量)的时候,就会启动扩容,每次扩容都会扩容为原来的两倍大小。
-
Hash冲突解决办法:JDK1.7底层采用链表法。
在JDK1.8版本中,为了对HashMap做进一步优化,我们引入了红黑树。而当链表长度太长(默认超过8)时,链表就转换为红黑树。我们可以利用红黑树快速增删改查的特点,提高HashMap的性能。当红黑树结点个数少于8个的时候,又会将红黑树转化为链表。因为在数据量较小的情况下,红黑树要维护平衡,比起链表来,性能上的优势并不明显。
-
Hash函数:JDK的hash函数非常经典,建议大家收藏,以后都可以用。
javaint hash(Object key) { int h = key.hashCode(); return (h ^ (h >>> 16)) & (capitity -1); //capicity表示散列表的大小,最好使用2的整数倍 }
6 如何设计Hash
假如你在面试中碰到了要你如何设计一个高效的企业级Hash表。你该如何处理?
这里大家可以借鉴HashMap 的设计思路:
- 必须要高效:即插入,删除 查找必须要快
- 内存:不能占用太多的内存,考虑用其他的结构,比如B+Tree,HashMap 10亿,存硬盘的算法:mysql B+tree
- Hash函数:这个要根据实际情况考虑.%
- 扩容:就是预估数据的大小,HashMap 默认的空间是16? 我知道我要存10000个数,2^n > 10000 or 2^n-1
- Hash冲突后怎么解决:链表 数组。
7 Hash的应用
-
加密:MD5 哈希算法。还是存在密码冲突 128位的二进制串,可以表示2^128次,md5(md5(),"1231"),b不可逆,建了一个hash库,存起来了。 Md5(88888888),穷举
-
怎么判断视频是否是重复的?Md5();128位
-
相似性检测:论文检测,指纹算法。会把每个论文计算出一个指纹,汉明距离。
-
负载均衡:nginx,2台服务器;可以根据ip计算hash,再做一个取模2运算。
-
分布系统:数据分库问题。我不是讲过一个10亿的数据的搜索词,一台机器存不下。要分成10个文件。Hash(key)%10 = > 就可以知道某个key在哪一个文件?扩大成数据库的 分表(10张表) id%10 =()。
-
分布式存储的时候:问题来了 如果我加了一张表。原来是10张,现在是11张了 怎么办?要重新计算,分配的时候查询怎么办?数据量很大,迁移的不是太多了吗?
-
查找算法:hashMap 查找如何设计出自己的一个hash查找算法呢?快,hash冲突要少,数据大小。初始大小,扩容
-
一致性Hash:哈希环
假设我们有k个表,数据的hash值范围:[0,Integer.max].我们把整个数据范围划分成n个区间(n个区间要远远大于我们的k),每一个表就是分配到n/k的区间。当有新的表要来了,我们只需要将某几个小的区间数据迁移就可以了。这是一个环形结构,其根本思想就是分段了。
重点部分:数组 链表 二叉树 红黑树 HashMap源码
8 HashMap源码(又双叒叕)
HashMap是Java集合框架中的一个重要类,用于存储键值对。
HashMap的一些核心常用方法的简要说明和示例代码,具体细节可能涉及到哈希碰撞、加载因子、扩容等实现细节,这些通常不需要开发者手动实现,但了解这些细节有助于更好地理解HashMap的工作原理。
构造方法:
java
HashMap<Integer, String> map = new HashMap<>(); // 创建一个空的HashMap
HashMap<Integer, String> mapWithCapacity = new HashMap<>(16); // 创建一个预设容量的HashMap
put方法:
java
map.put(1, "Apple"); // 添加键值对
get方法:
Java
String value = map.get(1); // 获取键对应的值
equals方法:
在Java中,
HashMap
的equals
方法用于比较两个HashMap
对象是否相等。equals
方法首先会检查两个对象的hashCode是否相等,如果hashCode不等,那么两个对象不可能相等,如果hashCode相等,那么HashMap
会进一步比较其中的元素。以下是
HashMap
的equals
方法的核心代码:
java
public boolean equals(Object o) {
if (o == this)
return true;
if (!(o instanceof Map))
return false;
Map<?,?> m = (Map<?,?>) o;
if (m.size() != size())
return false;
try {
Iterator<Entry<K,V>> i = entrySet().iterator();
while (i.hasNext()) {
Entry<K,V> e = i.next();
K key = e.getKey();
V value = e.getValue();
if (value == null) {
if (!(m.get(key)==null && m.containsKey(key)))
return false;
} else {
if (!value.equals(m.get(key)))
return false;
}
}
} catch (ClassCastException unused) {
return false;
} catch (NullPointerException unused) {
return false;
}
return true;
}
这段代码首先检查
o
是否等于this
,如果是,则直接返回true
。然后检查o
是否是一个Map
实例。如果不是,则直接返回false
。如果是,HashMap
会比较其大小,如果两个Map
的大小不等,则直接返回false
。接下来,HashMap
会迭代其所有的Entry
,并逐一检查o
中是否包含相同的键值对。如果有任何一个键值对不匹配,则返回false
。如果所有的键值对都匹配,则返回true
。这个方法是
HashMap
对象相等性的核心实现,它确保了两个具有相同键值对的HashMap
被认为是相等的。
-
remove方法:
javaString removedValue = map.remove(1); // 移除键值对并返回值
-
size方法:
javaint size = map.size(); // 获取HashMap的大小
-
isEmpty方法:
javaboolean isEmpty = map.isEmpty(); // 检查HashMap是否为空
containsKey方法:
java
boolean contains = map.containsKey(1); // 判断HashMap是否包含键1
-
containsValue方法:
javaboolean containsValue = map.containsValue("Apple"); // 检查HashMap中是否包含指定值
-
keySet方法:
javaSet<Integer> keySet = map.keySet(); // 获取HashMap中所有键的Set视图
### values方法:
```java
Collection<String> values = map.values(); // 获取HashMap中所有值的Collection视图
```
### entrySet方法:
```java
Set<Map.Entry<Integer, String>> entrySet = map.entrySet(); // 获取HashMap中所有键值对的Set视图
```
-
clear方法:
javamap.clear(); // 清空HashMap中的所有键值对
-
hashCode方法:
javaCollection<String> values = map.values(); // 获取HashMap中所有值的Collection视图
-
clone方法:
### hash方法(用于自定义对象作为键时):
```java
public class MyKey {
int value;
// 必须重写hashCode方法
@Override
public int hashCode() {
return Objects.hash(value);
}
// 必须重写equals方法
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (obj == null || getClass() != obj.getClass()) return false;
MyKey myKey = (MyKey) obj;
return value == myKey.value;
}
}
```
**注意** :以上代码示例假设`Integer`和`String`是键和值的类型,实际使用时可以替换为任何实现了`equals()`和`hashCode()`方法的对象。