共计 4575 个字符,预计需要花费 12 分钟才能阅读完成。
作者:小傅哥
博客:https://bugstack.cn
Github:https://github.com/fuzhengwei/CodeGuide/wiki
积淀、分享、成长,让本人和别人都能有所播种!????
一、前言
为什么你的代码一坨坨?其实来自你有那么多为什么你要这样写代码!
- 为什么你的代码那么多 for 循环?因为没有正当的数据结构和算法逻辑。
- 为什么你的代码那么多 ifelse?因为短少设计模式对业务场景的使用。
- 为什么你的程序利用简单对接艰难?因为没有良好的零碎架构拆分和布局。
- 为什么你的程序逻辑开发交付慢返工多?因为不具备某些业务场景的开发教训。
- 为什么你的程序展示都是看上去不说人话?因为没有产品思维都是程序员逻辑的体现。
最终 ,所有的这些不合理交错在一起,就是你能看到的一坨坨的代码! 所以 ,要想把代码写好、写美,写到本人违心重复观赏,那么根本须要你有肯定的:根底能力( 数据结构、算法逻辑、设计模式 )、利用能力( 零碎架构、开发教训 )、拓展能力( 产品思维),这三方面综合起来能力更好的开发程序。
但可能杠精会喊,我就写个 CRUD 要什么逻辑、什么数据结构,还算法?
但写 CRUD 并不一定业务需要是 CRUD,只是你的知识面和技术深度只能把它设计成 CRUD,用 ifelse 和 for 循环在一个类里重复粘贴复制罢了。
可能同样的需要交给他人手里,就会想的更多搭建的更加欠缺。就像:树上 10 只鸟开一枪还剩下几只,你会想到什么?比方:
- 手抢是无声的吗?
- 枪声大吗?
- 这个城市打鸟犯不犯法?
- 确定那只鸟被打死了?
- 树上的鸟有没有聋子?
- 有没有被关在笼子里或者绑在树上的鸟?
- 旁边还有其余树吗?
- 有残疾或者飞不动的鸟吗?
- 有怀孕肚子里的鸟吗?
- 打鸟的人眼睛花没花?
- 保障是 10 只吗?
- 有没有那种不怕死的鸟?
- 会不会一枪打死两只或者更多?
- 所有的鸟都能够自在流动飞离树以外吗?
- 打死当前挂在树上还是掉下来了?
所以,你还置信写程序只是简简单单的搞 CRUD 吗?接下来小傅哥再带着你搞几个例子看一看!
二、代码就是对数学逻辑的具体实现
数据结构:数组、链表、红黑树
算法逻辑:哈希、扰动函数、负载因子、拉链寻址、
其实咱们所开发的业务程序,哪怕是 CRUD 也都是对数学逻辑的具体实现过程。只不过简略的业务有简略的数学逻辑、简单的业务有简单的数学逻辑。数学逻辑是对数据结构的应用,(例如:把大象装进冰箱分几步
) 正当的数据的构造有利于数据逻辑的实现和复杂程度。
在咱们罕用的 API 中,HashMap 就是一个十分好的例子,既有十分好的数据结构的应用,也有弱小的数学逻辑的实现。为此也让 HashMap 成为开发过程中十分罕用的 API,当然也成为面试过程中最常问的技术点。
重点,HashMap 中波及的知识点十分多,包含数据结构的应用、数组、链表、红黑树,也包含算法逻辑的实现:哈希、扰动函数、负载因子、拉链寻址等等。而这些常识如果能够深刻的搞清楚,是齐全不须要死记硬背的,也不须要为了面试造火箭。就像如下问题:
- HashMap 怎么来的?因为有十分多业务开发中须要 key、value 的模式寄存获取数据。
- 为什么要用哈希计算下标呢?因为哈希值求计算出的 key 具备低碰撞性。
- 为什么还要加扰动函数呀?因为扰动函数能够让数据散列的平均,如果 HashMap 中的数据都碰撞成短链表,就会大大降低 HashMap 的索引性能。
- 为什么会有链表呢?因为无论如何都有会有节点碰撞的可能,碰撞后 HashMap 抉择拉链寻址的形式存放数据。当然在 ThreadLocal 中采纳的是斐波那契(Fibonacci)散列 + 凋谢寻址,感兴趣也能够看看。
- 为什么链表会转换树呢?因为工夫复杂度问题,链表的工夫复杂度是 O(n),越长越慢。
- 为什么树是红黑树呢?红黑树具备平衡性,也就是彩色节点是均衡的,均衡带来的成果就是管制整体树高,让工夫复杂度最终放弃在 O(logn),否则都是一丿的树就没意义了。
- 为什么有个负载因子呢?负载因子决定 HashMap 的高矮胖瘦,负载你能够了解成一辆卡车能装多少货,装的越多这一趟赚的也浏览危险也越高,装的越少跑的越快赚的也少。所以抉择了适当大小 0.75。
- 为什么 JDK8 优化了数据扩容时迁徙?那不就是因为计算哈希值求下标消耗工夫吗,曾经找到了数学法则,间接迁徙就能够了,进步性能。
看到了吗? HashMap 齐全就是对数据结构的综合应用,以及对数学逻辑的完满联合,才让咱们有了十分好用的 HashMap。这些常识的学习就能够技术迁徙到咱们本人业务开发中,把有些业务开发优化到十分不错的性能体现上。同时你的代码也值得加薪!
哈希下标
图 15-2 中波及到的下标地位寄存的数据,不是胡乱写的。是依照 HashMap 中的计算逻辑找到的固定地位值。代码如下:
for (int i = 1; i < 1000; i++) {String key = String.valueOf(i);
int hash = key.hashCode() ^ (key.hashCode() >>> 16);
int idx = (64 - 1) & hash;
if (idx == 2) {// System.out.println(i + "Idx:" + idx);
}
if (idx == 62) {System.out.println(i + "Idx:" + idx);
}
}
如果你须要英文的,那么能够跑 10 万单词的字典表。对于 HashMap 的内容小傅哥曾经整顿到面经手册中,链接:面经手册 • 拿大厂 Offer
三、得物(毒) 8 位随机抽奖码设计
1. 需要形容
图 15-3 是咱们模仿得物 APP 中对于抽奖码需要的款式图,核心技术点包含:
- 须要一个 8 位的随机码,全局惟一。
- 每个人能够取得多个这样的随机码,随机码浏览中奖概率越大。
- 随机码咱们这里的设计与毒 App 的展示模式略有不同,组成包含:大写字母、小写字母和数字。
在你没有看实现计划前,你能够先思考下这样的惟一的随机码该怎么去生成。
2. 实现计划
2.1 基于 Redis 生成
int codeId = RedisUtil.incr("codeUUID");
String UUID = String.format("%08d", codeId);
System.out.println(UUID);
// 测试后果
00000001
00000002
00000003
- 评分:⭐
- 计划:基于 Redis 的 incr 办法,全局自增从 0 开始,以上是伪代码。
- 点评:以上计划不可用,除了并不一定能保障全局自增和可靠性外,有一个很大的问题是你的程序自增,把 APP 有多少人加入流动的数据裸露了。
2.2 随机数生成
Random random = new Random();
StringBuffer code = new StringBuffer();
for (int i = 0; i < 8; i++) {int number = random.nextInt(3);
switch (number) {
case 0:
code.append((char) (random.nextInt(26) + 65)); // 65 ~ 90
break;
case 1:
code.append((char) (random.nextInt(26) + 97)); // 97 ~ 122
break;
case 2:
code.append((char) (random.nextInt(9) + 48)); // 48 ~ 97
break;
}
}
System.out.println(code.toString());
// 测试后果
qvY0Fqrk
8uyehK3H
U7z2v4qK
- 评分:⭐⭐
- 计划:基于随机数生成 8 位随机码,相当于 62^8 次幂,有将近百万亿的随机数。
- 点评:此计划在很多业务场景中都有应用,但这里的实现还有一个问题,就是随性后的不唯一性,尽管咱们晓得这么大体量很难呈现两个雷同的。但如果随着业务经营与日俱增的应用,终究会有两个一样的随机数,只有呈现就会是客诉。所以还须要保障唯一性,能够在随机数中退出年或者月的标记,依照这个体量落库用防重形式保障惟一。当然你还能够有其余的形式来保障惟一
2.3 基于雪花算法
final static char[] digits = { '0', '1', '3', '2', '4', '7', '6', '5', '8',
'z', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L',
'9', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l',
'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y',
'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y',
'Z', '0', '1', };
public static void main(String[] args) {SnowflakeIdWorker idWorker = new SnowflakeIdWorker(0, 0);
System.out.println(idWorker.nextId());
long code = idWorker.nextId();
char[] buf = new char[64];
int charPos = 64;
int radix = 1 << 6;
long mask = radix - 1;
do {buf[--charPos] = digits[(int) (code & mask)];
code >>>= 6;
} while (code != 0);
System.out.println(new String(buf, charPos, (64 - charPos)));
}
// 测试后果
uxdDQOG001
uxd8Uoj001
uxdERuG000
- 评分:⭐⭐⭐
- 计划:基于雪花算法的外围目标是,生成随机串的自身就是惟一值,那么就不须要思考重复性。只须要将惟一值转换为对应 64 进制的字符串组合就能够了。
- 点评:这里的思路很好,但有几个问题须要解决。首先是雪花算法的长度是 18 位,在转换为 64 位时会会有 10 位长的随机字符串组合,不满足要求。另外大写字母、小写字母和数字组合是 62 个,还短少 2 个不满足 64 个,所以须要前面补充两位,但这两位生成的组合数须要废除。那么,如果依照这个生成随机串且保障惟一的思路,就须要欠缺雪花算法,升高位数,在满足业务本身的状况下,管制生成长度。
实现计划 ,究竟不会一次就完满,还须要一直的优化欠缺。除此之外也会有很多其余的思路,例如电商生成订单号的计划也能够思考设计,另外你认为这就完事了?当你曾经工作多年,那么你每一天其实都在解决技术问题也是数学问题,产品的需要也更像是数学作业! 加油数学老师!
四、总结
- 好的程序实现离不开数据结构的设计、逻辑算法的欠缺、设计模式的考量,再配合合乎业务倒退和程序设计的架构能力搭建出更加正当的程序。
- 在学习的过程中不要刻意去背答案、背套路,那不是文科内容的学习形式。只有你更多的去实际、去验证,让懂了就是真的懂,才更加舒心!
- 本篇又扯到了这,想问一句你是胆怯 35 岁,还是胆怯本人能力不迭年龄增长?想学就把常识学透,你骗不了面试官,只能骗本人!
五、系列举荐
- 握草,你居然在代码里下毒!
- 一次代码评审,差点过不了试用期!
- 数据结构,HashCode 为什么应用 31 作为乘数?
- HashMap 外围常识,扰动函数、负载因子、扩容链表拆分,深度学习
- HashMap 数据插入、查找、删除、遍历,源码剖析