首页 » PHP教程 » php要求超长技巧_谈谈面试哈希表系列

php要求超长技巧_谈谈面试哈希表系列

访客 2024-11-23 0

扫一扫用手机浏览

文章目录 [+]

随着工程实践的积累, 逐步创造了自己当初的肤浅. 口试的切入点, 最好是大家所熟习的, 但又能从中深度挖掘/阐发和具有区分度的.

本文结合自己的工程实践, 来谈谈对哈希表的优化和实践的一些理解.

php要求超长技巧_谈谈面试哈希表系列

根本篇:

哈希表由一定大小的连续桶(bucket)构成, 借助散列函数映射到详细某个桶上. 当多个key/value对聚拢到同一桶时, 会蜕变构成一个链表.

php要求超长技巧_谈谈面试哈希表系列
(图片来自网络侵删)

哈希表构造有两个主要的参数, 容量大小(Capacity)和负载因子(LoadFactor). 两者的乘积 Capacity LoadFactor决定了哈希表rehash的触发条件.

以空间换韶光为核心思想, 确保其数据构造的访问韶光掌握在O(1).

哈希表隐蔽了内部细节, 而对外的利用则非常的大略. 只需定义key的hash函数和compare函数即可.

以Java为例, 其把默认的hash函数和equals函数置于顶层的Object基类中.

class Object {

public native int hashCode();

public boolean equals(Object obj) {

return (this == obj);

}

}

所有的子类, 须要重载hashCode和equals就能方便的利用哈希表.

进阶篇:

hash函数的选择需担保一定散列度, 这样才能充分利用空间. 事实上哈希表的利用者, 每每关注hash函数的快速打算和高散列度, 却忽略了其潜在的风险和危急.

1). hash碰撞攻击

前段韶光, php爆出hash碰撞的攻击漏洞. 其攻击事理, 大略可概括为: 特定的大量key组合, 让哈希表退化为链表访问, 进而拖慢处理速率, 要求堆积, 终极演化为谢绝做事状态.

详细可参考博文: PHP哈希表碰撞攻击事理.

大致的思路是利用php哈希表大小为2的幂次, 索引位置打算由 hash(key) % size(bucket) 转变为 hash(key) & (1^n - 1).

黑客(hacker)知晓time33算法和hash函数, 可以布局/网络特定的key系列, 使得其hash(key)为同一桶索引值. 通过post要求附带, 导致php布局超长链的哈希表.

实在如果能理解hash碰撞攻击的事理, 解释其对hash的冲突处理和哈希表本身的数据构造模型有了较深的理解了.

2). 分段锁机制

如果加锁是不可避免的选择, 那能否减少锁冲突的概率呢?

答案是肯定的, 不同桶之间的key/value操作彼此互不影响. 在此条件下, 对哈希桶进行分段加锁. 这样全局锁就退化为多个分段锁, 而锁冲突的概率由于分区的缘故原由, 降落至1/N (N为分段锁个数).

Java并发类中的ConcurrentHashMap也是采取类似的思想来实现, 不过比这繁芜多了.

难度篇:

哈希表单key的操作繁芜度为O(1), 性能非常精良. 但须要对哈希表进行迭代遍历其所有元素时, 其性能就非常的差. 究其缘故原由是各个key/value对分散在各个桶中, 彼此并无关联. 元素遍历转化为对哈希桶的全扫描.

那如果存在这样的需求, 既要担保O(1)的单key操作韶光繁芜度, 又要让迭代遍历的繁芜度为O(n) (n为哈希表的key/value对个数, 不是桶个数), 那如何去实现呢?

1). LinkedHashMap&LRU缓存

是否存在一个复合数据构造, 既有Hashmap的特性, 又具备DoubleLinkedList线性遍历的特色?

答案是肯定的, 该复合构培养是LinkedHashmap.

注: 依次添加key1, key2, ..., key6, 其按插入顺序构成一个双向列表.

一图胜千言, 该图很形象的描述了LinkedHashMap的构成. 可以这么认为: 每个hash entry的构造的根本上, 添加prev和next成员指针用于掩护双向列表. 实现就这么大略.

在工程实践中, 每每采取LinkedHashMap的变体来实现带LRU机制的Cache.

大略描述其操作流程:

(1). 查询/添加key, 则把该key/value对搁置于LRU行列步队的末端

(2). 若key/value对个数超过阈值时, 则选择把LRU行列步队的首元素淘汰掉.

仿照key5元素被调查, 成为最近的热点, 则内部的链接模型状态转变如下:

注: key5被访问后, 内部双向行列步队发生变动, 可以理解为删除key5, 然后再添加key5至末端.

JAVA实现带LRU机制的Cache非常的大略, 用如下代码片段描述下:

public class LRULinkedHashMap<K, V> extends LinkedHashMap<K, V> {

private int capacity = 1024;

public LRULinkedHashMap(int initialCapacity, float loadFactor, int lruCapacity) {

// access order=> true:访问顺序, false:插入顺序

super(initialCapacity, loadFactor, true);

this.capacity = lruCapacity;

}

@Override

protected boolean removeEldestEntry(Entry<K, V> eldest) {

if(size() > capacity) {

return true;

}

return false;

}

}

注: 须要把稳access order为true, 表示按访问顺序掩护. 利用Java编程的孩子真幸福.

当哈希表中的元素数量超过预定的阈值时, 就会触发rehash过程. 但是若此时的hash表已然很大, rehash的完全过程会壅塞做事很永劫光. 这对高可用高相应的做事是不可想象的灾害.

面对这种情形, 要么避免大数据量的rehash涌现, 预先对数据规模进行有效评估. 要么就连续优化哈希的rehash过程.

2). 0/1切换和渐进式rehash

redis的设计者给出了一个很好的办理方案, 便是0/1切换hash表+渐进式rehash.

其渐进的rehash把全体迁移过程拆分为多个细粒度的子过程, 同时0/1切换的hash表共存.

redis的rehash过程分两种办法:

• lazy rehashing: 在对dict操作的时候附带实行一个slot的rehash

• active rehashing:定时做个小韶光片的rehash

总结:

哈希表作为常用的数据构造, 被人所熟知. 但对其进一步的理解和挖掘, 须要真正的工程实践积累. 洗尽铅华始见真.

标签:

相关文章

执业药师试卷代码解码药师职业发展之路

执业药师在药品质量管理、用药安全等方面发挥着越来越重要的作用。而执业药师考试,作为进入药师行业的重要门槛,其试卷代码更是成为了药师...

PHP教程 2025-02-18 阅读1 评论0

心灵代码主题曲唤醒灵魂深处的共鸣

音乐,作为一种独特的艺术形式,自古以来就承载着人类情感的表达与传递。心灵代码主题曲,以其独特的旋律和歌词,唤醒了无数人的灵魂深处,...

PHP教程 2025-02-18 阅读0 评论0

探寻福建各市车牌代码背后的文化内涵

福建省,地处我国东南沿海,拥有悠久的历史和丰富的文化底蕴。在这片充满魅力的土地上,诞生了许多具有代表性的城市,每个城市都有自己独特...

PHP教程 2025-02-18 阅读1 评论0

探寻河北唐山历史与现代交融的城市之光

河北省唐山市,一座地处渤海之滨,拥有悠久历史和独特文化的城市。这里既是古丝绸之路的起点,也是中国近代工业的发源地。如今,唐山正以崭...

PHP教程 2025-02-18 阅读1 评论0