hascode和equals方法没有被覆盖 - put和get如何工作?

时间:2015-09-03 13:21:38

标签: java hashmap equals hashcode

我有一个班级学生和马克斯。 我使用Student Object作为HashMap的关键字并将其标记为值。 如果我不覆盖hashMap并且等于,它仍然可以正常工作。

我。如果没有覆盖equals()hashcode(),有人可以解释它在内部是如何工作的吗? II。如果我仅覆盖hashcode()

,该怎么办?

iii.what如果我仅覆盖equals()

class Student {

    String name;
    public String getLastName() {
        return lastName;
    }
    public void setLastName(String lastName) {
        this.lastName = lastName;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    String lastName;

    Student(String name, String lastName){
        this.name = name;
        this.lastName = lastName;
    }
    public String toString(){
        return(" Name : " + this.getName() + " Last Name : " + this.getLastName());
    }
}
class Marks {
    Student s;
    String marks;
    public Student getS() {
        return s;
    }
    public void setS(Student s) {
        this.s = s;
    }
    public String getMarks() {
        return marks;
    }
    public void setMarks(String marks) {
        this.marks = marks;
    }
    Marks (Student s, String marks){
        this.marks = marks;
        this.s = s;
    }
    public String toString(){
        return(" Marks : " + this.getMarks());
    }
}



public class Main {
    public static void main(String[] args) {
        Student s1 = new Student("Vishnu","Verma");
        Student s2 = new Student("Amit","Sharma");

        Marks m1 = new Marks(s1,"65%");
        Marks m2 = new Marks(s2,"67%");

        Map <Student,Marks>map = new HashMap<Student,Marks>();
        map.put(s1, m1);
        map.put(s2, m2);

        System.out.println(map);
    }
}

4 个答案:

答案 0 :(得分:2)

如果它们的引用相等,它将认为对象是相等的。即他们指向同一个物体。

答案 1 :(得分:1)

让我们来看看HashMap是如何运作的(在简单的情况下)。

首先我们创建哈希表:

  1. 我们创建了许多桶 - 比方说2
  2. 然后我们决定哪些哈希码在哪个桶中,在一个中说负,在另一个中说正(和零)。
  3. 当我们添加密钥时 - &gt;值映射到我们的HashMap我们:

    1. 计算密钥的hashCode
    2. 给定hashCode和上面的哈希表,我们选择一个桶
    3. 我们确定该存储桶中是否已存在某些内容,如果我们使用简单的数据结构(比如单链表)来存储多个密钥 - &gt;同一桶中的值映射。
    4. 要从我们的HashMap我们获取密钥值

      1. 计算密钥的hashCode
      2. 给定hashCode和上面的哈希表,我们选择一个桶
      3. 如果存储桶中有单个项目,我们会检查它们是否是映射equals到所请求密钥的键,如果是,则返回值
      4. 如果存储桶中有多个项目,我们会循环显示存储桶中的项目以查找第一个项目,其中键equals请求的键并返回其值
      5. 如果不存在此类映射,我们返回null
      6. 所以,回答你的问题:

        如果不覆盖equals()hashcode(),它内部如何处理?

        我认为你的意思是你不覆盖任何东西并将其保留为默认行为。

        在这种情况下,HashMap使用Object.hashCodeObject.equals; HashMap的行为与IdentityHashMap完全相同 - 即它会使用System.identityHashCode来计算hashCode,并使用==来测试相等性。

        此默认行为仅在处理class同一实例时才能正常工作。

        如果我仅覆盖hashcode()

        TL; DR:您的HashMap的行为与上述相同。

        hashCode将用于在插入(2)和检索(2)期间选择存储桶,但默认equals将用于确定是否找到了正确的密钥。

        Map可能与上述情况类似,因为hashCode实施不太可能违反hashCode的一般合同,即只有完全相同的实例才会等于它自己,如果hashCode编译的事实是它必须在同一个实例上多次调用时返回相同的值,它必须通过扩展符合这个要求。

        NB:相反的情况并非如此。由于equals返回不同哈希码的项目,仅覆盖hashCode会立即违反equals的合同。这会将基于散列的集合从HashMap分解为HashSet,因为您将拥有重复的密钥,并且集合将显示未定义的行为。

        这个问题是,除了你认为hashCode的对象之外,还有更多的对象会返回相同的equals - 虽然这是哈希本质的一部分,但你会复合这个问题。

        在这种情况下,您的HashMap可能效率低下。

        为了说明我的意思,请考虑一个带有一个元素的class的简单示例:

        class IntHolder {
            int value;
        
            //getter setter
        }
        

        如果我们使用默认equalshashCode,则我们的Map会出现以下行为:

        final Map<IntHolder, String> map = new HashMap<>();
        final IntHolder a = new IntHolder();
        a.setValue(7);
        final IntHolder b = a;
        final IntHolder c = new IntHolder();
        c.setValue(7);
        
        map.put(a, "A");
        System.out.println(map.get(a)); // --> A
        System.out.println(map.get(b)); // --> A
        System.out.println(map.get(c)); // --> null
        
        map.put(c, "C");
        System.out.println(map.get(a)); // --> A
        System.out.println(map.get(b)); // --> A
        System.out.println(map.get(c)); // --> C
        

        即。 Map只会考虑==的两个键(例如ab)。

        如果您“丢失”对密钥实例的引用,您将永远无法将其恢复。所以,如果你这样做了:

        c = null;
        

        如果没有循环Map,你就永远不会再次从Map中获得“C”。

        OP问我们如何确定只需要覆盖hashCode()或者只需要覆盖equals()?

        您应该只覆盖这些方法的

        从来没有用于仅覆盖equals或仅覆盖hashCode的用例。

答案 2 :(得分:0)

如果您的班级没有实施,那么hashCodeequals会回退到以下Object实施:

public native int hashCode();

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

因此hashCode的实现因运行时环境的不同而有所不同,但关于HashMap更有趣,equals方法要求两个对象是相同的实例才是相同的!

Object无法猜测是什么让两个实例相等,所以它默认为最强约束。

答案 3 :(得分:0)

如果不覆盖equals和hashcode,则实现继承自Object。

HashMap使用方法hashcode()来确定条目将在哪个子列表中。因此,如果您不覆盖(或者更糟糕的是,覆盖并为每个实例返回相同的值)方法,HashMap可能会将每个条目存储在同一个子列表中。这会使地图变慢,但输入值仍然有用。

有关上一个问题,请参阅https://docs.oracle.com/javase/8/docs/api/java/lang/Object.html#hashCode--

长话短说:你不应该仅仅覆盖一个。你应该覆盖它们。