笔者去面试的时候,面试官冷不丁地问“Java中两个对象怎么判断是不是同一个”,第一反应就是答:用 equals()。这个回答不能说错,那还需要重写什么方法,为什么,很容易被继续追问到发懵。因为面试官真正想问的是:你说的“同一个”,到底是地址一样、内容一样,还是放进 HashMap 里也能被当成同一个 key?
一句话结论: == 像是在问“是不是同一张身份证原件”,equals() 像是在问“身份证信息是不是同一个人”,hashCode() 像是在问“先去哪个柜子里找这个人”,三者回答的是不同层次的问题。
这篇不先堆术语。我们先用自己的话把这道题讲顺,再补一点源码和 Android 里的实际场景。你只要记住一个方向:判断对象相同,先问清楚你到底想判断哪种相同。
1. 面试官问“相同”,其实可能问三件事
比如有两个 User:
User a = new User(1, "张三");
User b = new User(1, "张三");
你说它俩相同吗?
这个问题要看你站在哪个角度:
| 是不是同一个对象 | 是不是同一个人站在这里 | 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年入国军吗,恰恰不是,我们要在这个时代站稳脚跟,是需要去打牢这些基础根基的。
网硕互联帮助中心





评论前必须登录!
注册