Nicksxs's Blog

What hurts more, the pain of hard work or the pain of regret?

Java 中的这个话题也是比较常遇到的,关于这块原先也是比较忽略的,但是仔细想想又有点遗忘了就在这里记一下
简单看下代码
java.lang.Object#equals

1
2
3
public boolean equals(Object obj) {
return (this == obj);
}

对于所有对象的父类,equals 方法其实对比的就是对象的地址,也就是是否是同一个对象,试想如果像 Integer 或者 String 这种,我们没有重写 equals,那其实就等于是在用==,可能就没法达到我们的目的,所以像 String 这种常用的 jdk 类都是默认重写了
java.lang.String#equals

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public boolean equals(Object anObject) {
if (this == anObject) {
return true;
}
if (anObject instanceof String) {
String anotherString = (String)anObject;
int n = value.length;
if (n == anotherString.value.length) {
char v1[] = value;
char v2[] = anotherString.value;
int i = 0;
while (n-- != 0) {
if (v1[i] != v2[i])
return false;
i++;
}
return true;
}
}
return false;
}

然后呢就是为啥一些书或者 effective java 中写了 equalshashCode 要一起重写,这里涉及到当对象作为 HashMapkey 的时候
首先 HashMap 会使用 hashCode 去判断是否在同一个槽里,然后在通过 equals 去判断是否是同一个 key,是的话就替换,不是的话就链表接下去,如果不重写 hashCode 的话,默认的 objecthashCodenative 方法,根据对象的地址生成的,这样其实对象的值相同的话,因为地址不同,HashMap 也会出现异常,所以需要重写,同时也需要重写 equals 方法,才能确认是同一个 key,而不是落在同一个槽的不同 key.

前几天同事问了我个 mysql 索引的问题,虽然大概知道,但是还是想来实践下,就是 is null,is not null 这类查询是否能用索引,可能之前有些网上的文章说都是不能用索引,但是其实不是,我们来看个小试验

1
2
3
4
5
6
7
8
9
10
CREATE TABLE `null_index_t` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`null_key` varchar(255) DEFAULT NULL,
`null_key1` varchar(255) DEFAULT NULL,
`null_key2` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `idx_1` (`null_key`) USING BTREE,
KEY `idx_2` (`null_key1`) USING BTREE,
KEY `idx_3` (`null_key2`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

用个存储过程来插入数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

delimiter $ #以delimiter来标记用$表示存储过程结束
create procedure nullIndex1()
begin
declare i int;
declare j int;
set i=1;
set j=1;
while(i<=100) do
while(j<=100) do
IF (i % 3 = 0) THEN
INSERT INTO null_index_t ( `null_key`, `null_key1`, `null_key2` ) VALUES (null , LEFT(MD5(RAND()), 8), LEFT(MD5(RAND()), 8));
ELSEIF (i % 3 = 1) THEN
INSERT INTO null_index_t ( `null_key`, `null_key1`, `null_key2` ) VALUES (LEFT(MD5(RAND()), 8), NULL, LEFT(MD5(RAND()), 8));
ELSE
INSERT INTO null_index_t ( `null_key`, `null_key1`, `null_key2` ) VALUES (LEFT(MD5(RAND()), 8), LEFT(MD5(RAND()), 8), NULL);
END IF;
set j=j+1;
end while;
set i=i+1;
set j=1;
end while;
end
$
call nullIndex1();

然后看下我们的 is null 查询

1
EXPLAIN select * from null_index_t WHERE null_key is null;


再来看看另一个

1
EXPLAIN select * from null_index_t WHERE null_key is not null;


从这里能看出来啥呢,可以思考下

从上面可以发现,is null应该是用上了索引了,所以至少不是一刀切不能用,但是看着is not null好像不太行额
我们在做一点小改动,把这个表里的数据改成 9100 条是 null,剩下 900 条是有值的,然后再执行下

然后再来看看执行结果

1
EXPLAIN select * from null_index_t WHERE null_key is null;

1
EXPLAIN select * from null_index_t WHERE null_key is not null;


是不是不一样了,这里再补充下我试验使用的 mysql 是 5.7 的,不保证在其他版本的一致性,
其实可以看出随着数据量的变化,mysql 会不会使用索引是会变化的,不是说 is not null 一定会使用,也不是一定不会使用,而是优化器会根据查询成本做个预判,这个预判尽可能会减小查询成本,主要包括回表啥的,但是也不一定完全准确。

几年前看了柴静的《穹顶之下》觉得这个记者调查得很深入,挺有水平,然后再看到了她跟丁仲礼的采访,其实没看完整,也没试着去理解,就觉得环境问题挺严重的,为啥柴静这个对面的这位好像对这个很不屑的样子,最近因为丁仲礼上了美国制裁名单,B 站又有人把这个视频发了出来,就完整看了下,就觉得自己挺惭愧的,就抱着对柴静的好感而没来由的否定了丁老的看法和说法,所以人也需要不断地学习,改正之前错误的观点,当然不是说我现在说的就是百分百正确,只是个人的一些浅显的见解

先聊聊这个事情,整体看下来我的一些理解,IPCC给中国的方案其实是个很大的陷阱,它里面有几个隐藏的点是容易被我们外行忽略的,第一点是基数,首先发达国家目前(指2010年采访或者IPCC方案时间)的人均碳排放量已经是远高于发展中国家的了,这也就导致了所谓的发达国家承诺减排80%是个非常有诚意的承诺其实就是忽悠;第二点是碳排放是个累计过程,从1900年开始到2050年,或者说到2010年,发达国家已经排的量是远超过发展中国家的,这是非常不公平的;第三点其实是通过前两点推导出来的,也就是即使发达国家这么有诚意地说减排80%,扒开这层虚伪的外衣,其实是给他们11亿人分走了48%的碳排放量,留给发展中国家55亿人口的只剩下了52%;第四点,人是否因为国家的发达与否而应受到不平等待遇,如果按国家维度,丁老说的,摩纳哥要跟中国分同样的排放量么,中国人还算不算人;第五点,这点算是我自己想的,也可能是柴静屁股决定脑袋想不到的点,她作为一个物质生活条件已经足够好了,那么对于那些生活在物质条件平均线以下的,他们是否能像城里人那样有空调地暖,洗澡有热水器浴霸,上下班能开车,这些其实都直接或者间接地导致了碳排放;他们有没有改善物质生活条件地权利呢,并且再说回来,其实丁老也给了我们觉得合理地方案,我们保证不管发达国家不管减排多少,我们都不会超过他们的80%,我觉得这是真正的诚意,而不是接着减排80%的噱头来忽悠人,也是像丁老这样的专家才能看破这个陷阱,碳排放权其实就是发展权,就是人权,中国人就不是人了么,或者说站在贫困线以下的人民是否有改善物质条件的权利,而不是说像柴静这样,只是管她自己,可能觉得小孩因为空气污染导致身体不好,所以做了穹顶之下这个纪录片,想去改善这个事情,空气污染不是说对的,只是每个国家都有这个过程,如果不发展,哪里有资源去让人活得好,活得好了是前提,然后再去各方面都改善。

对于这个问题其实更想说的是人的认知偏差,之前总觉得美帝是更自由民主,公平啥的,或者说觉得美帝啥都好,有种无脑愤青的感觉,外国的月亮比较圆,但是经历了像川普当选美国总统以来的各种魔幻操作,还有对于疫情的种种不可思议的美国民众的反应,其实更让人明白第一是外国的月亮没比较圆,第二是事情总是没那么简单粗暴非黑即白,美国不像原先设想地那么领先优秀,但是的确有很多方面是全球领先的,天朝也有体制所带来的优势,不可妄自菲薄,也不能忙不自大,还是要多学习知识,提升认知水平。

题目介绍

Given preorder and inorder traversal of a tree, construct the binary tree.
给定一棵树的前序和中序遍历,构造出一棵二叉树

注意

You may assume that duplicates do not exist in the tree.
你可以假设树中没有重复的元素。(PS: 不然就没法做了呀)

例子:

1
2
preorder = [3,9,20,15,7]
inorder = [9,3,15,20,7]

返回的二叉树

1
2
3
4
5
  3
/ \
9 20
/ \
15 7

简要分析

看到这个题可以想到一个比较常规的解法就是递归拆树,前序就是根左右,中序就是左根右,然后就是通过前序已经确定的根在中序中找到,然后去划分左右子树,这个例子里是 3,找到中序中的位置,那么就可以确定,9 是左子树,15,20,7是右子树,然后对应的可以根据左右子树的元素数量在前序中划分左右子树,再继续递归就行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
class Solution {
public TreeNode buildTree(int[] preorder, int[] inorder) {
// 获取下数组长度
int n = preorder.length;
// 排除一下异常和边界
if (n != inorder.length) {
return null;
}
if (n == 0) {
return null;
}
if (n == 1) {
return new TreeNode(preorder[0]);
}
// 获得根节点
TreeNode node = new TreeNode(preorder[0]);
int pos = 0;
// 找到中序中的位置
for (int i = 0; i < inorder.length; i++) {
if (node.val == inorder[i]) {
pos = i;
break;
}
}
// 划分左右再进行递归,注意下`Arrays.copyOfRange`的用法
node.left = buildTree(Arrays.copyOfRange(preorder, 1, pos + 1), Arrays.copyOfRange(inorder, 0, pos));
node.right = buildTree(Arrays.copyOfRange(preorder, pos + 1, n), Arrays.copyOfRange(inorder, pos + 1, n));
return node;
}
}

题目介绍

Design a stack that supports push, pop, top, and retrieving the minimum element in constant time.
设计一个栈,支持压栈,出站,获取栈顶元素,通过常数级复杂度获取栈中的最小元素

  • push(x) – Push element x onto stack.
  • pop() – Removes the element on top of the stack.
  • top() – Get the top element.
  • getMin() – Retrieve the minimum element in the stack.

示例

Example 1:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Input
["MinStack","push","push","push","getMin","pop","top","getMin"]
[[],[-2],[0],[-3],[],[],[],[]]

Output
[null,null,null,null,-3,null,0,-2]

Explanation
MinStack minStack = new MinStack();
minStack.push(-2);
minStack.push(0);
minStack.push(-3);
minStack.getMin(); // return -3
minStack.pop();
minStack.top(); // return 0
minStack.getMin(); // return -2

简要分析

其实现在大部分语言都自带类栈的数据结构,Java 也自带 stack 这个数据结构,所以这个题的主要难点的就是常数级的获取最小元素,最开始的想法是就一个栈外加一个记录最小值的变量就行了,但是仔细一想是不行的,因为随着元素被 pop 出去,这个最小值也可能需要梗着变化,就不太好判断了,所以后面是用了一个辅助栈。

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
class MinStack {
// 这个作为主栈
Stack<Integer> s1 = new Stack<>();
// 这个作为辅助栈,放最小值的栈
Stack<Integer> s2 = new Stack<>();
/** initialize your data structure here. */
public MinStack() {

}

public void push(int x) {
// 放入主栈
s1.push(x);
// 当 s2 是空或者当前值是小于"等于" s2 栈顶时,压入辅助最小值的栈
// 注意这里的"等于"非常必要,因为当最小值有多个的情况下,也需要压入栈,否则在 pop 的时候就会不对等
if (s2.isEmpty() || x <= s2.peek()) {
s2.push(x);
}
}

public void pop() {
// 首先就是主栈要 pop,然后就是第二个了,跟上面的"等于"很有关系,
// 因为如果有两个最小值,如果前面等于的情况没有压栈,那这边相等的时候 pop 就会少一个了,可能就导致最小值不对了
int x = s1.pop();
if (x == s2.peek()) {
s2.pop();
}
}

public int top() {
// 栈顶的元素
return s1.peek();
}

public int getMin() {
// 辅助最小栈的栈顶
return s2.peek();
}
}
0%