集合里的元素怎么“不见了”?

链接:/posts/3941

集合里的元素怎么“不见了”?

文章插图
程序员小乐(ID:)第802次推文 图片来自百度
往日回顾:小米回应暴力裁员:已提前三个月通知不续签合同,并且给了N+1补偿
正文
昨天花时间在 debug 一个非常诡异的问题 。Java 代码里面的一个集合里面命令包含这个元素,、 都一样,甚至对象的 id 都是一样的,但是方法返回的结果总是 false !最后花了很多时间,百思不得其解,一度怀疑我生活在里面 。最后发现问题的一刻也恍然大悟,发现这是一个我早就知道的问题 。这必定成为我职业生涯的一个污点,所以我打算记录一下这个问题 。
先卖个关子吧,我来描述一下问题的背景,看你能否想到答案 。
问题是这样的,我们用来解决一个图的问题 。这个图是我们从应用的调用关系链中生成的,生成之后会导出到 json,放到一个地方 。然后所有的计算节点都可以通过这个 json 来 load 图,就不用每个节点都去清洗一遍了 。一个节点清洗过后,所有的节点都从这里加载 。问题主要出现图的导出和导入,图中每个节点都有一个 id,一开始我用应用的名字作为 id,导出到 json,但是导入的时候发现会重新生成 ID,图的关系是对的,但是节点的 ID 从字符串变成了重新生成的 id 了,那么应用名字的信息就丢失了 。我又给节点加上 name 属性,期望这个属性之后还是好的 。结果发现只是图的关系,并没有进来其他属性(这个库看起来很 nice 啊,不知道为啥文档这么差, 的细节都没有文档) 。于是我参考 Test 里面的做法,用一个Map存下来节点的其他属性 。然后在完成之后,将这些属性set进去 。
OK,总结一下,简单来说就是,我先从 json 导入进图,导入的时候也存下来每个节点的属性(其实就是 name),导入之后遍历图的节点,将每个属性设置进去 。
问题就出现了 。我用图来找最短路径的时候报错:节点不存在!
定位到库里面,判断节点不存在的函数是这么写的:
集合里的元素怎么“不见了”?

文章插图
我 debug 了这个Set和v的关系,发现Set中的一个元素,跟v是一模一样的!对象id都是一样的 。
返回值是一样的:
集合里的元素怎么“不见了”?

文章插图
返回值也一样:
集合里的元素怎么“不见了”?

文章插图
但是这个函数就是返回false 。
为了让这个问题更明显一些,我把这个问题简化成下面这段Java 代码,可以直接运行:
import java.util.HashSet;import java.util.Objects;public class Vertex {private String id;private String name;public Vertex(String id, String name) {this.id = id;this.name = name;}public static void main(String[] args) {Vertex app1 = new Vertex("1", null);Vertex app2 = new Vertex("2", null);Vertex app3 = new Vertex("3", null);// 模拟我们从 json 载入这个图的过程// 这个时候 name 是不在图里面的HashSet sets = new HashSet<>();sets.add(app1);sets.add(app2);sets.add(app3);// 载入之后,我们会将属性设置好,欢迎应用名字的信息app1.name = "app1";app2.name = "app2";app3.name = "app3";// 返回 falseSystem.out.println(sets.contains(app1));System.out.println(sets.stream().filter(x -> x.hashCode() == app1.hashCode()).findFirst());System.out.println(sets.stream().filter(x -> x.equals(app1)).findFirst());}@Overridepublic boolean equals(Object o) {if (this == o) { return true; }if (o == null || getClass() != o.getClass()) { return false; }Vertex vertex = (Vertex) o;return Objects.equals(id, vertex.id) &&Objects.equals(name, vertex.name);}@Overridepublic int hashCode() {return Objects.hash(id, name);}}