程序员老鬼

外包偷吃了正编的下午茶,被公司定为恶性事件~

刚刷到个贴子,说深圳某公司发通告:外包人员偷吃了正编的下午茶,被定性为“恶性事件”,后续还要赔偿+隔离就餐区。😅

Image

作为程序员,我看这事有点无语。下午茶这点零食,说大不大,但被公司上升到“恶性事件”,其实折射的不是吃没吃的问题,而是身份和信任感的矛盾。在很多公司,外包本来就被边缘化,工位、权限、福利都分层,这一口茶歇成了压垮关系的稻草。

Image

网友有人笑说“何必小题大做”,也有人觉得“制度面前人人平等”。但换我说,程序员日常都讲权限管理,你把资源边界划清楚,别人自然不会踩雷。反过来说,如果制度模糊、心态又排斥,那迟早出问题。

归根结底,茶歇不是重点,尊重和规则才是核心。只要界限透明,沟通清楚,就算是外包,也能像团队的一份子。总的来说,制度是死的,人心是活的,别让小事撕裂了合作氛围。【备注:文末可领最新资料】

算法题:完全相等的 JSON 字符串

昨天晚上十一点多,在公司楼下吹风儿,手机那边小李又问:“哥,两个 JSON 字符串到底怎么判完全相等啊?为啥我 trim 了还不行?”我一口热奶茶差点洒了——这个事儿,别靠肉眼瞪,得按“结构”和“类型”来比,对吧。

就是那个…怎么说呢…不是看字符一模一样,而是解析后的值一样:

  • 类型要对上:对象对对象,数组对数组,数字别和字符串混。
  • 对象里键顺序不算事儿,但键集合和对应值要一一匹配。
  • 数组必须同长度、同位置相等,这个顺序不能乱。
  • 数字这块最坑:1 和 1.0 人看像,机器常当不一样。我一般把它们都拉到 BigDecimal 比一下精度。

思路就三步

第一步,把俩字符串解析成树;第二步,按类型递归比较;第三步,数字用 BigDecimal,对象按键集合遍历,数组按下标走。这样空格、换行、键顺序这些“表面功夫”就都忽略了。哦对,null 也是值,不能和 "null" 搞混。

有人问我那个配置怎么写…咳我先说坑:

  • 布尔和字符串 "true" 不一样;
  • 科学计数法 1e2 和 100 需要转成 BigDecimal 再比;
  • 不合法的 JSON 别逞强,直接抛错或者返回不相等;
  • Unicode 转义 "\u0041" 和 "A",大多数解析器会还原,解析后自然能对上。

我现在困得眼睛直眨,但代码给你们放这儿,就这个思路,落地很稳。

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.*;
import java.math.BigDecimal;
import java.util.Iterator;
import java.util.Map;
import java.util.TreeMap;

publicclassJsonDeepEqual{
privatestaticfinal ObjectMapper MAPPER = new ObjectMapper();

publicstaticbooleanjsonEqual(String a, String b){
try {
            JsonNode n1 = MAPPER.readTree(a);
            JsonNode n2 = MAPPER.readTree(b);
return deepEqual(n1, n2);
        } catch (JsonProcessingException e) {
// 非法 JSON,按需处理:这里直接认为不相等
returnfalse;
        }
    }

privatestaticbooleandeepEqual(JsonNode a, JsonNode b){
if (a == null || b == null) return a == b;
if (a.getNodeType() != b.getNodeType()) {
// 特判数字:不同数值节点类型也可能等
if (isNumber(a) && isNumber(b)) {
return numberEqual(a, b);
            }
returnfalse;
        }

switch (a.getNodeType()) {
case NULL: returntrue;
case BOOLEAN: return a.booleanValue() == b.booleanValue();
case NUMBER: return numberEqual(a, b);
case STRING: return a.textValue().equals(b.textValue());
case ARRAY:
if (a.size() != b.size()) returnfalse;
for (int i = 0; i < a.size(); i++) {
if (!deepEqual(a.get(i), b.get(i))) returnfalse;
                }
returntrue;
case OBJECT:
// 键顺序忽略:用排序后的视图比较
if (a.size() != b.size()) returnfalse;
                Map<String, JsonNode> ma = new TreeMap<>();
                Map<String, JsonNode> mb = new TreeMap<>();
                Iterator<Map.Entry<String, JsonNode>> ia = a.fields();
                Iterator<Map.Entry<String, JsonNode>> ib = b.fields();
while (ia.hasNext()) {
                    Map.Entry<String, JsonNode> e = ia.next();
                    ma.put(e.getKey(), e.getValue());
                }
while (ib.hasNext()) {
                    Map.Entry<String, JsonNode> e = ib.next();
                    mb.put(e.getKey(), e.getValue());
                }
if (!ma.keySet().equals(mb.keySet())) returnfalse;
for (String k : ma.keySet()) {
if (!deepEqual(ma.get(k), mb.get(k))) returnfalse;
                }
returntrue;
default:
return a.equals(b); // 保险兜底
        }
    }

privatestaticbooleanisNumber(JsonNode n){
return n != null && n.isNumber();
    }

privatestaticbooleannumberEqual(JsonNode a, JsonNode b){
        BigDecimal x = new BigDecimal(a.asText());
        BigDecimal y = new BigDecimal(b.asText());
return x.compareTo(y) == 0;
    }

// 小测一下
publicstaticvoidmain(String[] args){
        String s1 = "{\"a\":1,\"b\":[true, null, {\"x\":\"A\"}]}";
        String s2 = "{ \"b\" : [ true , null , {\"x\":\"\\u0041\"} ], \"a\" : 1.0 }";
        System.out.println(jsonEqual(s1, s2)); // true
    }
}

对了,如果你们遇到 NaN、Infinity 那些,标准 JSON 本来就不该有,直接当非法处理就省心了。

-END-

我为大家打造了一份RPA教程,完全免费:songshuhezi.com/rpa.html

最后给大家分享一份不错的副业资料,点击下方公众号,回复关键字: 副业 领取,也可以链接我领取,微信:hls404