目标:Java八股问题,是面试的必备,但以一个什么姿势学习八股题才能真的学会呢?才能做到让懂了就是很的懂,让会了就是真的会。也给小伙伴们一个白嫖到小傅哥技术的机会。
八股题实际上是Java核心技术知识的一部分,用于考察面试者对技术知识广度的了解和深度的认知。然而,大多数八股题的难度并不低,而很多资料也只是对特定问题给出答案,因此很多读者并没有在理解的基础上掌握这些内容,通常只能靠死记硬背。但这种方式既费时间,也收益不大。
那咋办😰,咋办?这根本就不是一个理科生该有的学习方式,我们的原则就一条,我们的看得见。不要只给我理论,我要过程、要过程。享受的不就是过程吗?打在弹幕上,告诉我你享受的是啥?是结果还是过程?
什么讨厌八股题,我是不讨厌八股题,那都是我那技术的老baby!每一个八股题几乎都对应一种编程算法的优雅设计,上能用到项目开发组件,下能搞定面试拿到Offer!
就像HashMap的数据结构是数组哈希桶+链表,使用的是乘法散列+拉链寻址。那么对应的ThreadLocal的数据结构是数组,使用的是斐波那契散列+开放寻址,
就像问你;HashMap用的是什么数据结构,采用的什么散列算法。ThreadLocal也是存放数据的,为什么采用斐波那契散列。**好好,到这接下来开始往技术应用上引。**那数据库路由为什么不使用斐波那契散列,它不是散列效果好吗?🤔

散列的算法有很多,包括:乘法散列、除法散列、平方散列、斐波那契散列等,但要说给数据库路由散列使用,那么就不是随便放个散列算法都可以的了,弄不好可能会造成线上事故。
为了能确保散列算法在某一场景使用的合理性,需要进行**严格雪崩标准(SAC)**测试,这样才能放心是否可以使用。那啥是雪崩测试?
在密码学中,雪崩效应是密码算法的理想属性,通常是分组密码和密码散列函数,其中如果输入发生轻微变化(例如,翻转单个位),输出会发生显着变化(例如,50%输出位翻转)
SAC 建立在完整性和雪崩的概念之上,由 Webster(韦伯斯特) 和 Tavares(塔瓦雷斯) 于 1985 年引入。SAC 的高阶概括涉及多个输入位。满足最高阶 SAC 的最大非线性函数,也称为“完全非线性”函数。
简单来说,当我们对数据库从8库32表扩容到16库32表的时候,每一个表中的数据总量都应该以50%的数量进行减少。这样才是合理的。「10万个单词用作样本数据。」
public Map<Integer, Map<Integer, Integer>> hashFunction(int dbCount, int tbCount, Long hashIncrementVal, int hashType) {
int size = dbCount * tbCount;
System.out.print("库数:" + dbCount + " 表数:" + tbCount + " 总值:" + size + " 幂值:" + Math.log(size) / Math.log(2));
int HASH_INCREMENT = (int) ((null == hashIncrementVal ? size : hashIncrementVal) * (Math.sqrt(5) - 1) / 2);
System.out.print(" 黄金分割:" + HASH_INCREMENT + "/" + size + " = " + (double) HASH_INCREMENT / size);
Map<Integer, Map<Integer, Integer>> map = new ConcurrentHashMap<>();
Set<String> words = FileUtil.readWordList("/Users/fuzhengwei/1024/github/java-algorithms/logic/src/main/java/math/fibonacci/103976个英语单词库.txt");
System.out.println(" 单词总数:" + words.size() + "\\r\\n");
for (String word : words) {
int idx = 0;
switch (hashType) {
// 散列:斐波那契散列 int idx = (size - 1) & (word.hashCode() * HASH_INCREMENT + HASH_INCREMENT);
case 0:
idx = (word.hashCode() * HASH_INCREMENT) & (size - 1);
break;
// 散列:哈希散列 + 扰动函数
case 1:
idx = (size - 1) & (word.hashCode() ^ (word.hashCode() >>> 16));
break;
// 散列:哈希散列
case 2:
idx = (size - 1) & (word.hashCode()/* ^ (word.hashCode() >>> 16)*/);
break;
// 散列:整数求模
case 3:
idx = Math.abs(word.hashCode()) % size;
break;
}
// 计算路由索引
int dbIdx = idx / tbCount + 1;
int tbIdx = idx - tbCount * (dbIdx - 1);
// 保存路由结果
if (map.containsKey(dbIdx)) {
Map<Integer, Integer> dbCountMap = map.get(dbIdx);
if (dbCountMap.containsKey(tbIdx)) {
dbCountMap.put(tbIdx, dbCountMap.get(tbIdx) + 1);
} else {
dbCountMap.put(tbIdx, 1);
}
} else {
Map<Integer, Integer> dbCountMap = new HashMap<>();
dbCountMap.put(tbIdx, 1);
map.put(dbIdx, dbCountMap);
}
}
return map;
}
斐波那契散列