Object、List、Map、Set与equals和hashCode的关系

本文深入解析Java中对象的相等性判断(equals方法)和哈希码(hashCode方法)的实现原则,阐述了在集合类如List、Set、Map中正确覆盖这两个方法的重要性,以及如何实现合理的equals和hashCode方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Object的equals()方法:

Object是所有类的基类,不是接口的基类。

1.使用List的contains()、indexOf()这些方法,集合中对象是需要实现equals()方法。
2.在Map的内部,对key做比较是通过equals()实现的,正确使用Map必须保证:作为key的对象必须正确覆写equals()方法
3.Set实际上相当于只存储key、不存储value的Map。最常用的Set实现类是HashSet,实际上,HashSet仅仅是对HashMap的一个简单封装。Set的元素和Map的key类似,都要正确实现equals()和hashCode()方法,否则该元素无法正确地放入Set。放入HashSet的元素与作为HashMap的key要求相同。

如何写equals方法

1.先确定实例“相等”的逻辑,即哪些字段相等,就认为实例相等;
2.用instanceof判断传入的待比较的Object是不是当前类型,如果是,继续比较,否则,返回false;
3.对引用类型用Objects.equals()比较,对基本类型直接用==比较。

注意:使用Objects.equals()比较两个引用类型是否相等的目的是省去了判断null的麻烦。两个引用类型都是null时它们也是相等的。
当equals()方法被override时,hashCode()也要被override



public class Person {
    String firstName;
    String lastName;
    int age;

	@Override
	public boolean equals(Object o) {
	    if (o instanceof Person) {
	        Person p = (Person) o;
	        return Objects.equals(this.firstName, p.firstName)
	        &&Objects.equals(this.lastName, p.lastName) 
	        && this.age == p.age;
	    }
	    return false;
	}
    @Override
    int hashCode() {
        int h = 0;
        h = 31 * h + firstName.hashCode();
        h = 31 * h + lastName.hashCode();
        h = 31 * h + age;
        return h;
    }
}

Object的hashCode的用处

hashcode存在每个对象的堆内存的头部,如果两个对象相等,则两个对象的hashCode()必须相等。如果两个对象不相等,则两个对象的hashCode()尽量不要相等。如果两个对象hashcode都不相等,那么可以判断两个对象不相等,如果两个对象hashcode相等可以继续通过equals方法进行进一步比较。
HashMap相同的key对象(使用equals()判断时返回true)必须要利用hashCode计算出相同的索引
对于hashmap的key如果两个对象相等,则两个对象的hashCode()必须相等
如果两个对象不相等,则两个对象的hashCode()尽量不要相等
equals()用到的用于比较的每一个字段,都必须在hashCode()中用于计算;equals()中没有使用到的字段,绝不可放在hashCode()中计算

如何写hashCode方法

int hashCode() {
    return Objects.hash(firstName, lastName, age);
}

一种利用set的去重算法

package com.example.test1;

import java.util.*;

public class DeReprocess {
    public static void main(String[] args) {
        List<Message> received = new ArrayList();
        received.add(new Message(1, "Hello!"));
        received.add(new Message(2, "发工资了吗?"));
        received.add(new Message(2, "发工资了吗?"));
        received.add(new Message(3, "去哪吃饭?"));
        received.add(new Message(3, "去哪吃饭?"));
        received.add(new Message(4, "Bye"));
        List<Message> displayMessages = process(received);
        for (Message message : displayMessages) {
            System.out.println(message.text);
        }
    }

    static List<Message> process(List<Message> received) {
        List<Message> messages = new ArrayList<>();

        Set<Message> sets = new HashSet<>();
        for (Message mess : received) {
            if (sets.add(mess)) {// sets按照 Message中eauals()方法的sequence和text去重
                messages.add(mess);
            }
        }
        received = messages;
        return received;
    }
}

class Message {
    public final int sequence;
    public final String text;

    public Message(int sequence, String text) {
        this.sequence = sequence;
        this.text = text;
    }

    public boolean equals(Object o) {
        if (this == o) {
            return true;
        }
        if (o instanceof Message) {
            Message m = (Message) o;

            boolean textEqualsFlag=false;

            if(text==null&&m.text==null){
                textEqualsFlag=true;
            }

            if(text!=null){
                textEqualsFlag=text.equals(m.text);
            }

            if (textEqualsFlag && sequence == m.sequence) {
                return true;
            }
        }
        return false;
    }

    public int hashCode() {
        return Objects.hash(sequence, text);
    }
}


SortedMap保证遍历时以Key的顺序来进行排序。使用TreeMap时,放入的Key必须实现Comparable接口。String、Integer这些类已经实现了Comparable接口,因此可以直接作为Key使用。如果作为Key的class没有实现Comparable接口,那么,必须在创建TreeMap时同时指定一个自定义排序算法:

public class Main {
    public static void main(String[] args) {
        Map<Student, Integer> map = new TreeMap<>(new Comparator<Student>() {
            public int compare(Student p1, Student p2) {
            	// 去掉注释,否则  System.out.println(map.get(new Student("Bob", 66)))为null
            	//if (p1.score == p2.score) {
        		//	return 0;
    			//}
                return p1.score > p2.score ? -1 : 1;
            }
        });
        map.put(new Student("Tom", 77), 1);
        map.put(new Student("Bob", 66), 2);
        map.put(new Student("Lily", 99), 3);
        for (Student key : map.keySet()) {
            System.out.println(key);
        }
        System.out.println(map.get(new Student("Bob", 66))); // null?
    }
}

class Student {
    public String name;
    public int score;
    Student(String name, int score) {
        this.name = name;
        this.score = score;
    }
    public String toString() {
        return String.format("{%s: score=%d}", name, score);
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值