云计算百科
云计算领域专业知识百科平台

为什么面试官总爱问Java中怎么判断两个对象是相同的

笔者去面试的时候,面试官冷不丁地问“Java中两个对象怎么判断是不是同一个”,第一反应就是答:用 equals()。这个回答不能说错,那还需要重写什么方法,为什么,很容易被继续追问到发懵。因为面试官真正想问的是:你说的“同一个”,到底是地址一样、内容一样,还是放进 HashMap 里也能被当成同一个 key?

一句话结论: == 像是在问“是不是同一张身份证原件”,equals() 像是在问“身份证信息是不是同一个人”,hashCode() 像是在问“先去哪个柜子里找这个人”,三者回答的是不同层次的问题。

这篇不先堆术语。我们先用自己的话把这道题讲顺,再补一点源码和 Android 里的实际场景。你只要记住一个方向:判断对象相同,先问清楚你到底想判断哪种相同。

1. 面试官问“相同”,其实可能问三件事

比如有两个 User:

User a = new User(1, "张三");
User b = new User(1, "张三");

你说它俩相同吗?

这个问题要看你站在哪个角度:

你想问什么怎么去理解Java 里常用什么
是不是同一个对象 是不是同一个人站在这里 a == b
内容是不是一样 两张表上的姓名、id 是不是一样 a.equals(b)
放进集合能不能当成同一个 通讯录里会不会重复存两份 equals() 加 hashCode()

#mermaid-svg-FI7QnSDH0GQO6UiI{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-FI7QnSDH0GQO6UiI .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-FI7QnSDH0GQO6UiI .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-FI7QnSDH0GQO6UiI .error-icon{fill:#552222;}#mermaid-svg-FI7QnSDH0GQO6UiI .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-FI7QnSDH0GQO6UiI .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-FI7QnSDH0GQO6UiI .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-FI7QnSDH0GQO6UiI .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-FI7QnSDH0GQO6UiI .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-FI7QnSDH0GQO6UiI .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-FI7QnSDH0GQO6UiI .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-FI7QnSDH0GQO6UiI .marker{fill:#333333;stroke:#333333;}#mermaid-svg-FI7QnSDH0GQO6UiI .marker.cross{stroke:#333333;}#mermaid-svg-FI7QnSDH0GQO6UiI svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-FI7QnSDH0GQO6UiI p{margin:0;}#mermaid-svg-FI7QnSDH0GQO6UiI .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-FI7QnSDH0GQO6UiI .cluster-label text{fill:#333;}#mermaid-svg-FI7QnSDH0GQO6UiI .cluster-label span{color:#333;}#mermaid-svg-FI7QnSDH0GQO6UiI .cluster-label span p{background-color:transparent;}#mermaid-svg-FI7QnSDH0GQO6UiI .label text,#mermaid-svg-FI7QnSDH0GQO6UiI span{fill:#333;color:#333;}#mermaid-svg-FI7QnSDH0GQO6UiI .node rect,#mermaid-svg-FI7QnSDH0GQO6UiI .node circle,#mermaid-svg-FI7QnSDH0GQO6UiI .node ellipse,#mermaid-svg-FI7QnSDH0GQO6UiI .node polygon,#mermaid-svg-FI7QnSDH0GQO6UiI .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-FI7QnSDH0GQO6UiI .rough-node .label text,#mermaid-svg-FI7QnSDH0GQO6UiI .node .label text,#mermaid-svg-FI7QnSDH0GQO6UiI .image-shape .label,#mermaid-svg-FI7QnSDH0GQO6UiI .icon-shape .label{text-anchor:middle;}#mermaid-svg-FI7QnSDH0GQO6UiI .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-FI7QnSDH0GQO6UiI .rough-node .label,#mermaid-svg-FI7QnSDH0GQO6UiI .node .label,#mermaid-svg-FI7QnSDH0GQO6UiI .image-shape .label,#mermaid-svg-FI7QnSDH0GQO6UiI .icon-shape .label{text-align:center;}#mermaid-svg-FI7QnSDH0GQO6UiI .node.clickable{cursor:pointer;}#mermaid-svg-FI7QnSDH0GQO6UiI .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-FI7QnSDH0GQO6UiI .arrowheadPath{fill:#333333;}#mermaid-svg-FI7QnSDH0GQO6UiI .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-FI7QnSDH0GQO6UiI .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-FI7QnSDH0GQO6UiI .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-FI7QnSDH0GQO6UiI .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-FI7QnSDH0GQO6UiI .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-FI7QnSDH0GQO6UiI .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-FI7QnSDH0GQO6UiI .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-FI7QnSDH0GQO6UiI .cluster text{fill:#333;}#mermaid-svg-FI7QnSDH0GQO6UiI .cluster span{color:#333;}#mermaid-svg-FI7QnSDH0GQO6UiI div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-FI7QnSDH0GQO6UiI .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-FI7QnSDH0GQO6UiI rect.text{fill:none;stroke-width:0;}#mermaid-svg-FI7QnSDH0GQO6UiI .icon-shape,#mermaid-svg-FI7QnSDH0GQO6UiI .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-FI7QnSDH0GQO6UiI .icon-shape p,#mermaid-svg-FI7QnSDH0GQO6UiI .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-FI7QnSDH0GQO6UiI .icon-shape .label rect,#mermaid-svg-FI7QnSDH0GQO6UiI .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-FI7QnSDH0GQO6UiI .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-FI7QnSDH0GQO6UiI .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-FI7QnSDH0GQO6UiI :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}

是不是同一个对象

内容是不是一样

集合里算不算同一个

两个对象是不是相同

你问的是哪种相同

看引用:用 ==

看内容:用 equals

看 key:equals 加 hashCode

先说清口径,再给答案

所以面试时别急着说“用 equals”。更稳的开头是:

要看你说的相同是哪种。如果是判断是不是同一个对象,用 ==;如果是判断业务内容是不是一样,用 equals();如果这个对象要放进 HashMap 或 HashSet,还得保证 hashCode() 和 equals() 是配套的。

这句话一说,面试官基本就知道你不是只背过 API。

2. ==:看的是“是不是同一个对象”

先看最普通的例子:

User a = new User(1, "张三");
User b = new User(1, "张三");
User c = a;

System.out.println(a == b); // false
System.out.println(a == c); // true

为什么 a == b 是 false?

因为 new 了两次,就像打印了两张内容一样的表。表上的内容一样,但它们不是同一张纸。

为什么 a == c 是 true?

因为 c = a,就像 a 和 c 都指着同一张纸。

#mermaid-svg-D1494VSFlb6A7aK4{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-D1494VSFlb6A7aK4 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-D1494VSFlb6A7aK4 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-D1494VSFlb6A7aK4 .error-icon{fill:#552222;}#mermaid-svg-D1494VSFlb6A7aK4 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-D1494VSFlb6A7aK4 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-D1494VSFlb6A7aK4 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-D1494VSFlb6A7aK4 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-D1494VSFlb6A7aK4 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-D1494VSFlb6A7aK4 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-D1494VSFlb6A7aK4 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-D1494VSFlb6A7aK4 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-D1494VSFlb6A7aK4 .marker.cross{stroke:#333333;}#mermaid-svg-D1494VSFlb6A7aK4 svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-D1494VSFlb6A7aK4 p{margin:0;}#mermaid-svg-D1494VSFlb6A7aK4 .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-D1494VSFlb6A7aK4 .cluster-label text{fill:#333;}#mermaid-svg-D1494VSFlb6A7aK4 .cluster-label span{color:#333;}#mermaid-svg-D1494VSFlb6A7aK4 .cluster-label span p{background-color:transparent;}#mermaid-svg-D1494VSFlb6A7aK4 .label text,#mermaid-svg-D1494VSFlb6A7aK4 span{fill:#333;color:#333;}#mermaid-svg-D1494VSFlb6A7aK4 .node rect,#mermaid-svg-D1494VSFlb6A7aK4 .node circle,#mermaid-svg-D1494VSFlb6A7aK4 .node ellipse,#mermaid-svg-D1494VSFlb6A7aK4 .node polygon,#mermaid-svg-D1494VSFlb6A7aK4 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-D1494VSFlb6A7aK4 .rough-node .label text,#mermaid-svg-D1494VSFlb6A7aK4 .node .label text,#mermaid-svg-D1494VSFlb6A7aK4 .image-shape .label,#mermaid-svg-D1494VSFlb6A7aK4 .icon-shape .label{text-anchor:middle;}#mermaid-svg-D1494VSFlb6A7aK4 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-D1494VSFlb6A7aK4 .rough-node .label,#mermaid-svg-D1494VSFlb6A7aK4 .node .label,#mermaid-svg-D1494VSFlb6A7aK4 .image-shape .label,#mermaid-svg-D1494VSFlb6A7aK4 .icon-shape .label{text-align:center;}#mermaid-svg-D1494VSFlb6A7aK4 .node.clickable{cursor:pointer;}#mermaid-svg-D1494VSFlb6A7aK4 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-D1494VSFlb6A7aK4 .arrowheadPath{fill:#333333;}#mermaid-svg-D1494VSFlb6A7aK4 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-D1494VSFlb6A7aK4 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-D1494VSFlb6A7aK4 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-D1494VSFlb6A7aK4 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-D1494VSFlb6A7aK4 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-D1494VSFlb6A7aK4 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-D1494VSFlb6A7aK4 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-D1494VSFlb6A7aK4 .cluster text{fill:#333;}#mermaid-svg-D1494VSFlb6A7aK4 .cluster span{color:#333;}#mermaid-svg-D1494VSFlb6A7aK4 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-D1494VSFlb6A7aK4 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-D1494VSFlb6A7aK4 rect.text{fill:none;stroke-width:0;}#mermaid-svg-D1494VSFlb6A7aK4 .icon-shape,#mermaid-svg-D1494VSFlb6A7aK4 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-D1494VSFlb6A7aK4 .icon-shape p,#mermaid-svg-D1494VSFlb6A7aK4 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-D1494VSFlb6A7aK4 .icon-shape .label rect,#mermaid-svg-D1494VSFlb6A7aK4 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-D1494VSFlb6A7aK4 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-D1494VSFlb6A7aK4 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-D1494VSFlb6A7aK4 :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}

真正的对象在堆里

变量保存的是引用

a

b

c

User 对象一

User 对象二

a 和 c 指向同一个对象

a 和 b 指向两个对象

一句话:

== 不关心对象里面写了什么,只关心左右两边是不是指向同一个对象。

3. equals():看的是“你怎么定义内容一样”

那 equals() 呢?

很多人会说:equals() 比较内容。

这句话只说对了一半。更准确地说:

equals() 比较什么,取决于这个类有没有重写,以及它重写时怎么写。

默认情况下,普通类继承的是 Object.equals()。它的逻辑非常简单:

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

也就是说,普通类如果不重写 equals(),那 equals() 和 == 几乎一样,还是在问“是不是同一个对象”。

比如:

class User {
int id;
String name;

User(int id, String name) {
this.id = id;
this.name = name;
}
}

User a = new User(1, "张三");
User b = new User(1, "张三");

System.out.println(a.equals(b)); // false

你看,内容一样,但结果还是 false。因为 User 没有告诉 Java:“我认为 id 一样就算同一个用户。”

4. String.equals() 为什么能比较文字内容

String 就不一样了。它自己重写了 equals()。

String a = new String("hello");
String b = new String("hello");

System.out.println(a == b); // false
System.out.println(a.equals(b)); // true

a == b 是 false,因为这是两个对象。

a.equals(b) 是 true,因为 String.equals() 比较的是里面的字符是不是一样。

你可以把 String.equals() 想成这样:

// 简化理解,不是逐行源码
if (两个引用就是同一个对象) {
return true;
}
if (对方不是 String) {
return false;
}
return 一个字符一个字符比较;

所以不要背“equals() 就是比较内容”。应该背这个:

equals() 默认不一定比较内容。像 String 这种类是因为它重写了 equals(),所以才比较内容。

5. 字符串和包装类为什么总喜欢出陷阱题

字符串常量池

String s1 = "hello";
String s2 = "hello";
String s3 = new String("hello");

System.out.println(s1 == s2); // true
System.out.println(s1 == s3); // false
System.out.println(s1.equals(s3)); // true

这怎么理解?

"hello" 这种字面量会放到字符串常量池里。s1 和 s2 指向同一个池子里的字符串,所以 s1 == s2 是 true。

new String("hello") 是重新创建了一个对象,所以 s1 == s3 是 false。

但它们文字内容一样,所以 s1.equals(s3) 是 true。

Integer 缓存

Integer a = 127;
Integer b = 127;
Integer c = 128;
Integer d = 128;

System.out.println(a == b); // 通常是 true
System.out.println(c == d); // 通常是 false

这个坑更容易让人误会。常见 JDK 会缓存 -128 到 127 的 Integer 对象。127 命中了缓存,所以两个变量可能指向同一个对象;128 通常没有命中,就可能是两个对象。

但结论不是“Integer 小于 128 就用 ==”。结论应该是:

包装类型判断数值相等,别依赖 ==。要么拆成基本类型比较,要么用 equals()。

6. 如果你想让两个 User 内容一样就算相同,该怎么写

假设业务里规定:只要 id 一样,就认为是同一个用户。

那你就要在 equals() 里写清楚:

public final class User {
private final long id;
private final String name;

public User(long id, String name) {
this.id = id;
this.name = name;
}

@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (!(obj instanceof User)) {
return false;
}
User other = (User) obj;
return this.id == other.id;
}

@Override
public int hashCode() {
return Long.hashCode(id);
}
}

我们来拆解一下:

如果它俩本来就是同一个对象,直接返回 true。
如果对方根本不是 User,直接返回 false。
如果对方也是 User,那就比较 id。
id 一样,就当作同一个用户。

这里故意只比较 id,没有比较 name。因为很多业务里,用户改昵称之后,他还是同一个用户。

这就是面试官喜欢追问的地方:
你不是在写一个死板公式,你是在定义“什么叫同一个业务对象”。

7. 为什么写了 equals() 还要写 hashCode()

这一段是很多人真正掉坑的地方。

先想一个生活例子:

你去快递柜取快递。系统先告诉你:去 3 号柜。你去了 3 号柜,再看里面是不是你的包裹。

hashCode() 就像“先告诉你去哪个柜子找”。
equals() 就像“到柜子里以后确认是不是你的包裹”。

#mermaid-svg-3BjnCB4GE2Kykwab{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-3BjnCB4GE2Kykwab .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-3BjnCB4GE2Kykwab .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-3BjnCB4GE2Kykwab .error-icon{fill:#552222;}#mermaid-svg-3BjnCB4GE2Kykwab .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-3BjnCB4GE2Kykwab .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-3BjnCB4GE2Kykwab .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-3BjnCB4GE2Kykwab .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-3BjnCB4GE2Kykwab .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-3BjnCB4GE2Kykwab .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-3BjnCB4GE2Kykwab .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-3BjnCB4GE2Kykwab .marker{fill:#333333;stroke:#333333;}#mermaid-svg-3BjnCB4GE2Kykwab .marker.cross{stroke:#333333;}#mermaid-svg-3BjnCB4GE2Kykwab svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-3BjnCB4GE2Kykwab p{margin:0;}#mermaid-svg-3BjnCB4GE2Kykwab .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-3BjnCB4GE2Kykwab .cluster-label text{fill:#333;}#mermaid-svg-3BjnCB4GE2Kykwab .cluster-label span{color:#333;}#mermaid-svg-3BjnCB4GE2Kykwab .cluster-label span p{background-color:transparent;}#mermaid-svg-3BjnCB4GE2Kykwab .label text,#mermaid-svg-3BjnCB4GE2Kykwab span{fill:#333;color:#333;}#mermaid-svg-3BjnCB4GE2Kykwab .node rect,#mermaid-svg-3BjnCB4GE2Kykwab .node circle,#mermaid-svg-3BjnCB4GE2Kykwab .node ellipse,#mermaid-svg-3BjnCB4GE2Kykwab .node polygon,#mermaid-svg-3BjnCB4GE2Kykwab .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-3BjnCB4GE2Kykwab .rough-node .label text,#mermaid-svg-3BjnCB4GE2Kykwab .node .label text,#mermaid-svg-3BjnCB4GE2Kykwab .image-shape .label,#mermaid-svg-3BjnCB4GE2Kykwab .icon-shape .label{text-anchor:middle;}#mermaid-svg-3BjnCB4GE2Kykwab .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-3BjnCB4GE2Kykwab .rough-node .label,#mermaid-svg-3BjnCB4GE2Kykwab .node .label,#mermaid-svg-3BjnCB4GE2Kykwab .image-shape .label,#mermaid-svg-3BjnCB4GE2Kykwab .icon-shape .label{text-align:center;}#mermaid-svg-3BjnCB4GE2Kykwab .node.clickable{cursor:pointer;}#mermaid-svg-3BjnCB4GE2Kykwab .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-3BjnCB4GE2Kykwab .arrowheadPath{fill:#333333;}#mermaid-svg-3BjnCB4GE2Kykwab .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-3BjnCB4GE2Kykwab .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-3BjnCB4GE2Kykwab .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-3BjnCB4GE2Kykwab .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-3BjnCB4GE2Kykwab .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-3BjnCB4GE2Kykwab .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-3BjnCB4GE2Kykwab .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-3BjnCB4GE2Kykwab .cluster text{fill:#333;}#mermaid-svg-3BjnCB4GE2Kykwab .cluster span{color:#333;}#mermaid-svg-3BjnCB4GE2Kykwab div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-3BjnCB4GE2Kykwab .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-3BjnCB4GE2Kykwab rect.text{fill:none;stroke-width:0;}#mermaid-svg-3BjnCB4GE2Kykwab .icon-shape,#mermaid-svg-3BjnCB4GE2Kykwab .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-3BjnCB4GE2Kykwab .icon-shape p,#mermaid-svg-3BjnCB4GE2Kykwab .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-3BjnCB4GE2Kykwab .icon-shape .label rect,#mermaid-svg-3BjnCB4GE2Kykwab .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-3BjnCB4GE2Kykwab .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-3BjnCB4GE2Kykwab .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-3BjnCB4GE2Kykwab :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}

一个对象要放进 HashMap 或 HashSet

先算 hashCode

根据 hash 找到桶

桶里可能有多个对象,再用 equals 确认

是不是同一个 key

覆盖或去重

同桶但不是同一个对象

所以规则是:

如果两个对象 equals 是 true,
那么它们的 hashCode 必须一样。

反过来不一定:

两个对象 hashCode 一样,
equals 不一定是 true。

因为不同包裹可能刚好被分到了同一个柜子,这叫 hash 冲突。

8. 只写 equals() 不写 hashCode() 会发生什么

看这个错误写法:

public final class User {
private final long id;

public User(long id) {
this.id = id;
}

@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (!(obj instanceof User)) return false;
return this.id == ((User) obj).id;
}
}

你觉得下面集合里有几个元素?

Set<User> users = new HashSet<>();
users.add(new User(1));
users.add(new User(1));

System.out.println(users.size());

你业务上会觉得应该是 1。因为两个用户 id 都是 1。

但实际可能是 2。

为什么?

因为你只告诉 Java:“两个 id 一样的 User 算相等。”
但你没有告诉哈希集合:“它们应该去同一个桶里找。”

#mermaid-svg-QV9blz8RuW9cvd2C{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-QV9blz8RuW9cvd2C .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-QV9blz8RuW9cvd2C .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-QV9blz8RuW9cvd2C .error-icon{fill:#552222;}#mermaid-svg-QV9blz8RuW9cvd2C .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-QV9blz8RuW9cvd2C .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-QV9blz8RuW9cvd2C .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-QV9blz8RuW9cvd2C .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-QV9blz8RuW9cvd2C .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-QV9blz8RuW9cvd2C .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-QV9blz8RuW9cvd2C .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-QV9blz8RuW9cvd2C .marker{fill:#333333;stroke:#333333;}#mermaid-svg-QV9blz8RuW9cvd2C .marker.cross{stroke:#333333;}#mermaid-svg-QV9blz8RuW9cvd2C svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-QV9blz8RuW9cvd2C p{margin:0;}#mermaid-svg-QV9blz8RuW9cvd2C .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-QV9blz8RuW9cvd2C .cluster-label text{fill:#333;}#mermaid-svg-QV9blz8RuW9cvd2C .cluster-label span{color:#333;}#mermaid-svg-QV9blz8RuW9cvd2C .cluster-label span p{background-color:transparent;}#mermaid-svg-QV9blz8RuW9cvd2C .label text,#mermaid-svg-QV9blz8RuW9cvd2C span{fill:#333;color:#333;}#mermaid-svg-QV9blz8RuW9cvd2C .node rect,#mermaid-svg-QV9blz8RuW9cvd2C .node circle,#mermaid-svg-QV9blz8RuW9cvd2C .node ellipse,#mermaid-svg-QV9blz8RuW9cvd2C .node polygon,#mermaid-svg-QV9blz8RuW9cvd2C .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-QV9blz8RuW9cvd2C .rough-node .label text,#mermaid-svg-QV9blz8RuW9cvd2C .node .label text,#mermaid-svg-QV9blz8RuW9cvd2C .image-shape .label,#mermaid-svg-QV9blz8RuW9cvd2C .icon-shape .label{text-anchor:middle;}#mermaid-svg-QV9blz8RuW9cvd2C .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-QV9blz8RuW9cvd2C .rough-node .label,#mermaid-svg-QV9blz8RuW9cvd2C .node .label,#mermaid-svg-QV9blz8RuW9cvd2C .image-shape .label,#mermaid-svg-QV9blz8RuW9cvd2C .icon-shape .label{text-align:center;}#mermaid-svg-QV9blz8RuW9cvd2C .node.clickable{cursor:pointer;}#mermaid-svg-QV9blz8RuW9cvd2C .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-QV9blz8RuW9cvd2C .arrowheadPath{fill:#333333;}#mermaid-svg-QV9blz8RuW9cvd2C .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-QV9blz8RuW9cvd2C .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-QV9blz8RuW9cvd2C .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-QV9blz8RuW9cvd2C .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-QV9blz8RuW9cvd2C .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-QV9blz8RuW9cvd2C .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-QV9blz8RuW9cvd2C .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-QV9blz8RuW9cvd2C .cluster text{fill:#333;}#mermaid-svg-QV9blz8RuW9cvd2C .cluster span{color:#333;}#mermaid-svg-QV9blz8RuW9cvd2C div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-QV9blz8RuW9cvd2C .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-QV9blz8RuW9cvd2C rect.text{fill:none;stroke-width:0;}#mermaid-svg-QV9blz8RuW9cvd2C .icon-shape,#mermaid-svg-QV9blz8RuW9cvd2C .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-QV9blz8RuW9cvd2C .icon-shape p,#mermaid-svg-QV9blz8RuW9cvd2C .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-QV9blz8RuW9cvd2C .icon-shape .label rect,#mermaid-svg-QV9blz8RuW9cvd2C .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-QV9blz8RuW9cvd2C .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-QV9blz8RuW9cvd2C .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-QV9blz8RuW9cvd2C :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}

new User 一:id 为 1

new User 二:id 为 1

默认 hashCode 一

默认 hashCode 二

进入桶一

进入桶二

HashSet 没有机会比较 equals

最后留下两个看起来相同的用户

这就是为什么常说:

只要重写 equals(),通常就必须重写 hashCode()。

不然 HashMap、HashSet 这类集合就可能表现得很奇怪。

9. HashMap 查 key,其实就是两步

HashMap 找一个 key,可以理解成:

第一步:先根据 hashCode 找柜子。
第二步:柜子里再用 equals 确认是不是它。

简化伪代码:

// 简化理解,不是逐行源码
V get(Object key) {
int hash = key.hashCode();
Bucket bucket = findBucket(hash);

for (Node node : bucket) {
if (node.hash == hash &&
(node.key == key || key.equals(node.key))) {
return node.value;
}
}
return null;
}

注意这句:

node.key == key || key.equals(node.key)

意思是:

如果本来就是同一个对象,那肯定是同一个 key。
如果不是同一个对象,再看看 equals 认为它们是不是同一个 key。

这也是面试官喜欢从 equals() 追到 HashMap 的原因。它不是突然换题,而是顺着同一条线往下问。

10. 可变对象当 key,很容易把自己坑住

再看一个真实工作里会踩的坑。

User user = new User(1);
Map<User, String> map = new HashMap<>();
map.put(user, "张三");

user.setId(2);

System.out.println(map.get(user)); // 可能拿不到

为什么?

你可以想成:

放进去的时候,id 是 1,所以放进了 1 号柜。
后来你把 id 改成 2。
再去取的时候,系统让你去 2 号柜找。
可东西还在 1 号柜里。
所以找不到。

#mermaid-svg-w0dsLk1KNTnNfV1y{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-w0dsLk1KNTnNfV1y .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-w0dsLk1KNTnNfV1y .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-w0dsLk1KNTnNfV1y .error-icon{fill:#552222;}#mermaid-svg-w0dsLk1KNTnNfV1y .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-w0dsLk1KNTnNfV1y .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-w0dsLk1KNTnNfV1y .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-w0dsLk1KNTnNfV1y .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-w0dsLk1KNTnNfV1y .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-w0dsLk1KNTnNfV1y .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-w0dsLk1KNTnNfV1y .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-w0dsLk1KNTnNfV1y .marker{fill:#333333;stroke:#333333;}#mermaid-svg-w0dsLk1KNTnNfV1y .marker.cross{stroke:#333333;}#mermaid-svg-w0dsLk1KNTnNfV1y svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-w0dsLk1KNTnNfV1y p{margin:0;}#mermaid-svg-w0dsLk1KNTnNfV1y .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-w0dsLk1KNTnNfV1y .cluster-label text{fill:#333;}#mermaid-svg-w0dsLk1KNTnNfV1y .cluster-label span{color:#333;}#mermaid-svg-w0dsLk1KNTnNfV1y .cluster-label span p{background-color:transparent;}#mermaid-svg-w0dsLk1KNTnNfV1y .label text,#mermaid-svg-w0dsLk1KNTnNfV1y span{fill:#333;color:#333;}#mermaid-svg-w0dsLk1KNTnNfV1y .node rect,#mermaid-svg-w0dsLk1KNTnNfV1y .node circle,#mermaid-svg-w0dsLk1KNTnNfV1y .node ellipse,#mermaid-svg-w0dsLk1KNTnNfV1y .node polygon,#mermaid-svg-w0dsLk1KNTnNfV1y .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-w0dsLk1KNTnNfV1y .rough-node .label text,#mermaid-svg-w0dsLk1KNTnNfV1y .node .label text,#mermaid-svg-w0dsLk1KNTnNfV1y .image-shape .label,#mermaid-svg-w0dsLk1KNTnNfV1y .icon-shape .label{text-anchor:middle;}#mermaid-svg-w0dsLk1KNTnNfV1y .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-w0dsLk1KNTnNfV1y .rough-node .label,#mermaid-svg-w0dsLk1KNTnNfV1y .node .label,#mermaid-svg-w0dsLk1KNTnNfV1y .image-shape .label,#mermaid-svg-w0dsLk1KNTnNfV1y .icon-shape .label{text-align:center;}#mermaid-svg-w0dsLk1KNTnNfV1y .node.clickable{cursor:pointer;}#mermaid-svg-w0dsLk1KNTnNfV1y .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-w0dsLk1KNTnNfV1y .arrowheadPath{fill:#333333;}#mermaid-svg-w0dsLk1KNTnNfV1y .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-w0dsLk1KNTnNfV1y .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-w0dsLk1KNTnNfV1y .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-w0dsLk1KNTnNfV1y .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-w0dsLk1KNTnNfV1y .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-w0dsLk1KNTnNfV1y .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-w0dsLk1KNTnNfV1y .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-w0dsLk1KNTnNfV1y .cluster text{fill:#333;}#mermaid-svg-w0dsLk1KNTnNfV1y .cluster span{color:#333;}#mermaid-svg-w0dsLk1KNTnNfV1y div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-w0dsLk1KNTnNfV1y .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-w0dsLk1KNTnNfV1y rect.text{fill:none;stroke-width:0;}#mermaid-svg-w0dsLk1KNTnNfV1y .icon-shape,#mermaid-svg-w0dsLk1KNTnNfV1y .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-w0dsLk1KNTnNfV1y .icon-shape p,#mermaid-svg-w0dsLk1KNTnNfV1y .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-w0dsLk1KNTnNfV1y .icon-shape .label rect,#mermaid-svg-w0dsLk1KNTnNfV1y .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-w0dsLk1KNTnNfV1y .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-w0dsLk1KNTnNfV1y .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-w0dsLk1KNTnNfV1y :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}

put 时 id 是 1

按 id 为 1 放进桶一

中途把 id 改成 2

get 时去桶二找

桶二没有这个对象

对象其实还在桶一

所以经验是:

如果一个对象要当 HashMap 的 key,参与 equals() 和 hashCode() 的字段尽量不要变。

11. Objects.equals() 只是帮你少写空判断

有时你会看到这种写法:

Objects.equals(a, b)

它大概等于:

a == b || (a != null && a.equals(b))

它的好处是:不怕 a 是 null。

但它不是魔法。它不会帮你决定什么叫“业务上相等”。最终还是要看 a.equals(b) 自己怎么写。

12. Android 里这个问题特别常见

这题不是只在 Java 面试里出现。Android 里也经常遇到。

RecyclerView 列表刷新

DiffUtil 会问两个问题:

public class UserDiff extends DiffUtil.ItemCallback<User> {
@Override
public boolean areItemsTheSame(User oldItem, User newItem) {
return oldItem.getId() == newItem.getId();
}

@Override
public boolean areContentsTheSame(User oldItem, User newItem) {
return oldItem.equals(newItem);
}
}

这两个方法其实很像今天讲的内容。

areItemsTheSame:
这是不是同一条数据?
常见就是比较 id。

areContentsTheSame:
这条数据的展示内容有没有变?
可以比较整个对象内容。

#mermaid-svg-41Unc4IlauqK8zYP{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-41Unc4IlauqK8zYP .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-41Unc4IlauqK8zYP .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-41Unc4IlauqK8zYP .error-icon{fill:#552222;}#mermaid-svg-41Unc4IlauqK8zYP .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-41Unc4IlauqK8zYP .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-41Unc4IlauqK8zYP .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-41Unc4IlauqK8zYP .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-41Unc4IlauqK8zYP .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-41Unc4IlauqK8zYP .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-41Unc4IlauqK8zYP .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-41Unc4IlauqK8zYP .marker{fill:#333333;stroke:#333333;}#mermaid-svg-41Unc4IlauqK8zYP .marker.cross{stroke:#333333;}#mermaid-svg-41Unc4IlauqK8zYP svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-41Unc4IlauqK8zYP p{margin:0;}#mermaid-svg-41Unc4IlauqK8zYP .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-41Unc4IlauqK8zYP .cluster-label text{fill:#333;}#mermaid-svg-41Unc4IlauqK8zYP .cluster-label span{color:#333;}#mermaid-svg-41Unc4IlauqK8zYP .cluster-label span p{background-color:transparent;}#mermaid-svg-41Unc4IlauqK8zYP .label text,#mermaid-svg-41Unc4IlauqK8zYP span{fill:#333;color:#333;}#mermaid-svg-41Unc4IlauqK8zYP .node rect,#mermaid-svg-41Unc4IlauqK8zYP .node circle,#mermaid-svg-41Unc4IlauqK8zYP .node ellipse,#mermaid-svg-41Unc4IlauqK8zYP .node polygon,#mermaid-svg-41Unc4IlauqK8zYP .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-41Unc4IlauqK8zYP .rough-node .label text,#mermaid-svg-41Unc4IlauqK8zYP .node .label text,#mermaid-svg-41Unc4IlauqK8zYP .image-shape .label,#mermaid-svg-41Unc4IlauqK8zYP .icon-shape .label{text-anchor:middle;}#mermaid-svg-41Unc4IlauqK8zYP .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-41Unc4IlauqK8zYP .rough-node .label,#mermaid-svg-41Unc4IlauqK8zYP .node .label,#mermaid-svg-41Unc4IlauqK8zYP .image-shape .label,#mermaid-svg-41Unc4IlauqK8zYP .icon-shape .label{text-align:center;}#mermaid-svg-41Unc4IlauqK8zYP .node.clickable{cursor:pointer;}#mermaid-svg-41Unc4IlauqK8zYP .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-41Unc4IlauqK8zYP .arrowheadPath{fill:#333333;}#mermaid-svg-41Unc4IlauqK8zYP .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-41Unc4IlauqK8zYP .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-41Unc4IlauqK8zYP .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-41Unc4IlauqK8zYP .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-41Unc4IlauqK8zYP .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-41Unc4IlauqK8zYP .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-41Unc4IlauqK8zYP .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-41Unc4IlauqK8zYP .cluster text{fill:#333;}#mermaid-svg-41Unc4IlauqK8zYP .cluster span{color:#333;}#mermaid-svg-41Unc4IlauqK8zYP div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-41Unc4IlauqK8zYP .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-41Unc4IlauqK8zYP rect.text{fill:none;stroke-width:0;}#mermaid-svg-41Unc4IlauqK8zYP .icon-shape,#mermaid-svg-41Unc4IlauqK8zYP .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-41Unc4IlauqK8zYP .icon-shape p,#mermaid-svg-41Unc4IlauqK8zYP .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-41Unc4IlauqK8zYP .icon-shape .label rect,#mermaid-svg-41Unc4IlauqK8zYP .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-41Unc4IlauqK8zYP .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-41Unc4IlauqK8zYP .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-41Unc4IlauqK8zYP :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}

旧列表里的用户

新列表里的用户

id 是否一样

内容是否一样

不是同一条:插入或删除

同一条且内容没变:不用刷新

同一条但内容变了:刷新这一项

这就是非常典型的“同一个”和“内容一样”分开判断。

Kotlin data class

Kotlin 里的 data class 会自动生成 equals() 和 hashCode():

data class User(
val id: Long,
val name: String
)

这很方便。但也要注意:

data class 默认会把主构造里的字段都拿去比较。

如果你在列表里判断“是不是同一条数据”,通常只应该看 id。
如果你判断“内容有没有变”,才更适合比较整个对象。

13. 为什么面试官这么爱问这个

因为这个问题表面很小,实际可以看出很多东西。

面试官继续问他想看什么
== 和 equals() 区别 你懂不懂引用和对象
Object.equals() 默认做什么 你有没有看过基础源码
String 为什么可以比较内容 你知不知道类可以重写规则
为什么还要 hashCode() 你懂不懂 HashMap / HashSet
只写 equals() 会怎样 你有没有踩过集合的坑
对象字段变了会怎样 你有没有真实工程经验
DiffUtil 怎么写 你能不能把 Java 基础用到 Android 里

说白了,它不是在问你会不会背 equals(),而是在看你有没有把“对象、集合、业务身份”这三件事连起来。

14. 面试时怎么答最顺

你可以这样说:

判断两个对象是否相同,要先看相同的含义。

如果是判断是不是同一个对象,用 ==。
因为 == 比较的是引用地址。

如果是判断内容是否相同,用 equals。
但普通类默认 equals 其实也是 this == obj,
所以需要根据业务重写 equals。

如果对象要放进 HashMap 或 HashSet,
还必须重写 hashCode。
因为 HashMap 会先用 hashCode 找桶,
再用 == 或 equals 判断是不是同一个 key。

所以 equals 相等的对象,hashCode 必须相同。
但 hashCode 相同,不一定 equals 相同。

更口语一点:

我一般会先问清楚这个“相同”指什么。如果只是两个变量是不是指着同一个对象,那就是 ==。如果是两个用户是不是业务上的同一个用户,就看 equals() 怎么定义,比如用用户 id 来比较。但只要这个对象要放到 HashMap 或 HashSet 里,就不能只写 equals(),还要写 hashCode()。因为哈希集合是先根据 hash 找桶,再在桶里用 equals() 确认。否则两个业务上相等的对象可能被放到不同桶里,导致去重失败或者查不到。

这个回答已经够应付大多数追问。

15. 几个常见小题

题目一

String a = new String("x");
String b = new String("x");

System.out.println(a == b);
System.out.println(a.equals(b));

答案:

false
true

因为 a 和 b 是两个对象,所以 == 是 false。它们文字内容一样,所以 equals() 是 true。

题目二

Integer a = 127;
Integer b = 127;
Integer c = 128;
Integer d = 128;

System.out.println(a == b);
System.out.println(c == d);

常见结果:

true
false

但你不要记成“127 用 ==,128 不行”。正确理解是:包装类型比较数值别依赖 ==,要用 equals() 或拆箱比较。

题目三

Set<User> set = new HashSet<>();
set.add(new User(1));
set.add(new User(1));

为什么可能有两个?

因为 User 可能只写了 equals(),没写 hashCode()。两个对象业务上相等,但去了不同桶,所以集合没法去重。

16. 最后总结

这道题其实一句话就能收住:

== 看是不是同一个对象;
equals 看业务上是不是相等;
hashCode 帮哈希集合先找到该去哪儿查。

别把它们混成一个东西。你只要能说清“我现在判断的是哪一种相同”,再讲出 HashMap 为什么要同时依赖 hashCode() 和 equals(),这题就不是坑,而是一个很好的加分点。

最后我的建议是,先把概念回答上来,再去用自己的话产出相应的理解,与面试官产生共鸣点,那么就容易得分,否则,基础概念不牢靠,人家就没有被问下去的意愿了,就非常容易失分的。AI时代下,现在的面试真的跟千军万马过独木桥一样,你不够优秀,哪怕有一丁点回答地不够到位,就容易被横向对比。这也是我们为什么还需要学这么基础的,是49年入国军吗,恰恰不是,我们要在这个时代站稳脚跟,是需要去打牢这些基础根基的。

赞(0)
未经允许不得转载:网硕互联帮助中心 » 为什么面试官总爱问Java中怎么判断两个对象是相同的
分享到: 更多 (0)

评论 抢沙发

评论前必须登录!