equal和hashcode

equal和hashcode


核心代码示例

public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) 
        {
            return false;
        }
        Brand brand = (Brand) o;
        return Objects.equals(name, brand.name);
    }
    @Override
    public int hashCode() {
        return Objects.hash(name);
    }

hashCode() 方法

  • hashCode() 的作用是为对象生成一个数字“指纹”(哈希码),这个“指纹”主要用于提高在基于哈希的集合(如 HashSet、HashMap)中的查找速度.

哈希码

哈希码是一个 int 类型的整数。理想情况下,相等的对象应该拥有相同的哈希码。不相等的对象,它们的哈希码最好也不同(但这不是强制要求,只是为了让性能更好)。


哈希码原理

  1. 计算哈希码: 当你要添加一个新对象(比如 new Brand("BMW"))时,HashSet 首先调用这个对象的 hashCode() 方法,得到一个数字,比如 123。
  2. 定位“桶”: HashSet 内部其实是一个数组,它用这个哈希码(123)通过一个算法计算出这个对象应该存放在数组的哪个位置(这个位置被称为“桶”或“bucket”)。
    快速判断:
  3. 比较: 如果这个“桶”是空的,说明这个对象肯定不存在,直接放进去。整个过程连一次 equals() 调用都不需要;如果这个“桶”里已经有对象了(这种情况叫“哈希冲突”),这时才需要启动 equals() 方法,让新对象和桶里的那几个对象逐一比较,看是否真的相等。

equals()与hashcode()的联系

  • Java 规定,这两个方法必须遵守一个非常重要的契约:
    如果两个对象通过 equals() 方法比较是相等的,那么调用这两个对象中任意一个对象的hashCode() 方法都必须产生相同的整数结果。
    反过来则不成立:如果两个对象的 hashCode() 相同,它们不一定 equals()(这就是哈希冲突)。
    所以在修改判断标准时,equals和hashcode要共同重写。

图形展示

graph TD subgraph "HashSet 内部结构 (一个哈希表)" direction LR subgraph "内部数组 (桶数组 Bucket Array)" direction TB B0["桶 0<br>Index: 0"] B1["桶 1<br>Index: 1"] B2["桶 2<br>Index: 2"] B3["桶 3<br>Index: 3"] B4["桶 4<br>Index: 4"] B5["桶 5<br>Index: 5"] B6["桶 6<br>Index: 6"] Bn["..."] end subgraph "桶 2 中的内容 (哈希冲突)" direction TB Node1["对象: new Brand('Apple')<br>hashCode: 102"] Node2["对象: new Brand('Google')<br>hashCode: 286"] Node3["对象: new Brand('Microsoft')<br>hashCode: 470"] end subgraph "桶 5 中的内容 (链表结构)" direction TB Node4["对象: new Brand('Ford')<br>hashCode: 789"] Node5["对象: new Brand('Audi')<br>hashCode: 789"] end subgraph "桶 6 中的内容 (红黑树结构)" direction TB TreeRoot["(根节点)<br>new Brand('Toyota')"] TreeLeft["(左子节点)<br>new Brand('BYD')"] TreeRight["(右子节点)<br>new Brand('Honda']"] TreeRoot --> TreeLeft TreeRoot --> TreeRight end end %% --- 连接关系 --- B2 --> Node1 Node1 --> Node2 Node2 --> Node3 B5 --> Node4 Node4 --> Node5 B6 --> TreeRoot %% --- 样式定义 --- classDef bucket fill:#e1f5fe,stroke:#01579b,stroke-width:2px,color:#000; classDef object fill:#fff9c4,stroke:#f57f17,stroke-width:2px,color:#000; classDef tree fill:#c8e6c9,stroke:#1b5e20,stroke-width:2px,color:#000; classDef empty fill:#f5f5f5,stroke:#9e9e9e,stroke-width:1px,stroke-dasharray: 5 5,color:#9e9e9e; class B0,B1,B3,B4,B7,Bn bucket; class B2,B5,B6 bucket; class Node1,Node2,Node3,Node4,Node5 object; class TreeRoot,TreeLeft,TreeRight tree; class B0,B1,B3,B4,B7,Bn empty;

完整的示例代码

点击查看代码
import java.util.*;
public class Main {
	public static void main(String[] args) {
		Scanner scanner = new Scanner(System.in);
		String line = scanner.nextLine();
		int n = Integer.parseInt(line);
		List<Car> cars = new ArrayList<>();
		List<Brand> brands = new ArrayList<>();
		for(int i=0;i<n;i++) {
			line = scanner.nextLine();
            Brand brand = new Brand(line);
			if(!brands.contains(brand)) {//应用
				brands.add(brand);
				cars.add(new Car(brand));
			}else {
				System.out.println(line+" exists");
			}
		}
		for(Car car:cars) {
			System.out.println(car);
		}
		System.out.println(ArrayList.class.getClassLoader());
		scanner.close();
	}
}
class Brand{
	private String name;
	public Brand(String name) {
		this.name = name;
	}
	public String toString() {
		return "Brand [name="+name+"]";
	}
	public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) //判断相同类
        {
            return false;
        }
        Brand brand = (Brand) o;
        return Objects.equals(name, brand.name);
    }
    @Override
    public int hashCode() {
        return Objects.hash(name);
    }
}
class Car{
	int id;
	Brand brand;
	private static int counter=0;

	static {
		System.out.println("0 car");
	}
	public Car(Brand brand) {
		this.brand = brand;
		 this.id = ++counter;
	}
	public String toString() {
		return "Car [id="+id+"brand="+brand.toString()+"]";
	}
}

posted @ 2025-10-21 21:54  穗和  阅读(3)  评论(0)    收藏  举报