0%

## 聊聊 Java 的 equals 和 hashCode 方法

Posted on 2021-01-03 Views: Disqus:

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

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

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

    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` 中写了 `equals` 跟 `hashCode` 要一起重写，这里涉及到当对象作为 `HashMap` 的 `key` 的时候
首先 `HashMap` 会使用 `hashCode` 去判断是否在同一个槽里，然后在通过 `equals` 去判断是否是同一个 `key`，是的话就替换，不是的话就链表接下去，如果不重写 `hashCode` 的话，默认的 `object` 的`hashCode` 是 `native` 方法，根据对象的地址生成的，这样其实对象的值相同的话，因为地址不同，`HashMap` 也会出现异常，所以需要重写，同时也需要重写 `equals` 方法，才能确认是同一个 `key`，而不是落在同一个槽的不同 `key`.

## 聊聊 mysql 索引的一些细节

Posted on 2020-12-27 In Mysql , C , 索引 , Mysql Views: Disqus:

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

    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;

用个存储过程来插入数据

    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 查询

    EXPLAIN select * from null_index_t WHERE null_key is null;

再来看看另一个

    EXPLAIN select * from null_index_t WHERE null_key is not null;

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

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

    EXPLAIN select * from null_index_t WHERE null_key is null;

    EXPLAIN select * from null_index_t WHERE null_key is not null;

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

## 从丁仲礼被美国制裁聊点啥

Posted on 2020-12-20 In 生活 , 吐槽 , 疫情 , 美国 Views: Disqus:

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

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

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

## Leetcode 105 从前序与中序遍历序列构造二叉树(Construct Binary Tree from Preorder and Inorder Traversal) 题解分析

Posted on 2020-12-13 In Java , leetcode , Binary Tree , java , Binary Tree , DFS Views: Disqus:

## 题目介绍

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

### 注意

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

### 例子:

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

返回的二叉树

      3
     / \
    9  20
      /  \
     15   7

## 简要分析

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

    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;
        }
    }

## Leetcode 155 最小栈(Min Stack) 题解分析

Posted on 2020-12-06 In Java , leetcode , stack , java , stack Views: Disqus:

## 题目介绍

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:

    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 出去，这个最小值也可能需要梗着变化，就不太好判断了，所以后面是用了一个辅助栈。

## 代码

    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();
            }
        }