本人最近做了1道题目,颇为疑惑
Given:
11. public class Person {
12. private int name;
13. public Person(String name) {
14. this.name = name;
15. }
16. public boolean equals(Object o) {
17. if( !o instanceof Person ) return false;
18. Person p = (Person) o;
19. return p.name.equals(this.name);
20. }
21. }
Which is true?
A. Compilation fails because the hashCode method is not overridden.
B. A HashSet could contain multiple Person objects with the same
name.
C. All Person objects will have the same hash code because the
hashCode method is not overridden.
D. If a HashSet contains more than one Person object with
name=”Fred”, then removing another Person, also with name=”Fred”,
will remove them all.答案是B但是我不这么认为,我知道的是HashSet和Set一样,是以equals方法来判断元素的唯一性的,也就是对于一个HashSet来说,任何其中的对象彼此调用 equals(Object) 方法都应该返回FALSE,概括来说,就是 相等的对象必须有相同的hashCode码,当然了,逆命题不成立,完全有可能2个对象有相同的hashCode(),但是不是相等对象
本题中:既然选择B,就是说,一个HashSet可以包含多个有相同name的Person对象,但是按照第16行---第20行,这些Person对象彼此调用equals(Object o) ,应该都返回true吧,那么不是和HashSet的唯一性矛盾了??
也许我钻牛角尖了,,希望大家给点意见。谢谢。。

解决方案 »

  1.   

    更正一下 。第12行应该为 private String name,我打字打错了
      

  2.   

    散列是根据hashCode()来进行的,不是依据equals()
      

  3.   

    1。!o instanceof Person这个表达式是错误的,需要在!后面加括号。
    2。HashSet内部使用了 HashMap,put方法是根据hashcode来计算存放位置的,自己想一下hash表的原理吧public V put(K key, V value) {
    if (key == null)
        return putForNullKey(value);
            int hash = hash(key.hashCode());
            int i = indexFor(hash, table.length);
            for (Entry<K,V> e = table[i]; e != null; e = e.next) {
                Object k;
                if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
                    V oldValue = e.value;
                    e.value = value;
                    e.recordAccess(this);
                    return oldValue;
                }
            }        modCount++;
            addEntry(hash, key, value, i);
            return null;
        }
      

  4.   

    但是,首先HashSet是一个Set阿,作为Set就必须满足唯一性要求,我看JAVA 文档,所有的对象都必须不相等阿,这样的话,只有equals 才是能否放入其中的依据吧,连防都没放进去,(这个前提都没满足)也就不谈它被散列到什么位置了。
      

  5.   

     
    关于第一点。。我也知道,题目本身就是这样的。这样根本通不过编译。。
    关于2:我想,Hash表原理我是明白的。。(PS.高中就学信息学了)可是我还是不知道如何解决这题目,请详细讲解下好么?
      

  6.   

    你自己创建几个Person加入看看
      

  7.   

    代码全部/**
     * 
     */
    package com.sun.scjptest;import java.util.*;
    /**
     * @author super
     *
     */
    public class Person {
    private String name;
    public Person(String name) {
     this.name = name;
     }
     public boolean equals(Object o) {
     if( !(o instanceof Person) ) return false;
     Person p = (Person) o;
     return p.name.equals(this.name);
     }
     public static void main(String [] args){
     Set<Person> set = new HashSet<Person>();
     Person p1= new Person("郭靖");
     Person p2= new Person("郭靖");
     Person p3= new Person("黄蓉");
     Person p4= new Person("郭襄");
     set.add(p1);
     set.add(p2);
     set.add(p3);
     set.add(p4);
     
     System.out.println(set.size());
     }
    }运行结果  4果然,是同一个hashSet可以包含多个同名对象,比如 p1和p2,但是我就是不明白WHY????
      

  8.   

    PS 如果在System.out.println(set.size());后面加上System.out.println(p1.equals(p2)); 结果是true,也就是说,p1和p2是相等对象,但是都被存储在了以唯一性而闻名中外的 HashSet中。
      

  9.   

    懂hash表的原理,put方法应该很容易看明白吧。
    因为使用的Object类的hashcode()来计算hash,所以不同的对象得到的hashcode肯定是不会冲突的,因为这个是根据地址来计算的。
    这个值不同,放入的位置就不同
    。。
      

  10.   

    关于JAVA 文档上的hashSet的原文
    ublic class HashSet<E>
    extends AbstractSet<E>
    implements Set<E>, Cloneable, SerializableThis class implements the Set interface, backed by a hash table (actually a HashMap instance). It makes no guarantees as to the iteration order of the set; in particular, it does not guarantee that the order will remain constant over time. This class permits the null element.This class offers constant time performance for the basic operations (add, remove, contains and size), assuming the hash function disperses the elements properly among the buckets. Iterating over this set requires time proportional to the sum of the HashSet instance's size (the number of elements) plus the "capacity" of the backing HashMap instance (the number of buckets). Thus, it's very important not to set the initial capacity too high (or the load factor too low) if iteration performance is important. 
      

  11.   

    这就是要求重写了equals方法一定要重写hashcode方法的原因。
      

  12.   


    我知道,的确hashCode在Object类的实现是按照比较内存地址来实现的。如果自己不覆盖,那么不同的对象,因为内存地址一定不同,当然是被HASH到不同的位置。。但是,我主要疑惑在于。HashSet首先必须实现Set接口阿,,那么Set的唯一性必须满足吧,但是如果比如我例子中的p1和p2,他们是equal的,但是被同时插入了hashSet中,这个是不是违背了唯一性原则
      

  13.   

    我晓得嘛。一般编程肯定重写这2方法的。可是有些人出题总是出非常规的东西,比如重写equals但是不重写hashCode,还让它保持Object中定义的原来面貌。虽然平时用不到这些异常情况,但是往往出题的人就考这些非正常的东西。。
      

  14.   

    其实,我关键的疑惑在于,,只重写equals方法,但是不覆盖hashCode方法,是否违背唯一性 原则。我最近在做SCJP练习,相关的题目我都全对的,应该对这2方法理解没什么偏差,至于这题目,我也是按照排除法做出来的,但是总感觉不光彩,想正面思考一下嘛。。做题是做题,学知识才是根本目的,呵呵。
      

  15.   

    if (e.hash == hash && key.equals(k))//另外的key省略了
    引用企鹅的程序分析,可以得知Set是根据hashCode()与equals()方法来判断Set中的2个对象时候
    相等(需要2个条件都满足)。
    我们知道Person p1= new Person("郭靖"); Person p2= new Person("郭靖"); 虽然看起来一样,
    不过他们在堆栈中的存储位置肯定不同(因为是不同的对象嘛)而且equals()方法没有覆盖,也只是
    Object根类的equals()方法 ,比较引用。
    综合上面所说,再看一个程序:
    import java.util.*;
    class Persion{
    private final int id;
    public Persion(int id){
    this.id=id;
    }
    public int hashCode(){
    return 1;
    }
    public String toString(){
    return super.toString()+id;
    }
    public boolean equals(Object o){
    return true;
    }
    }
    public class qdb{
    public static void main(String[] args){
    HashSet<Persion> hs1=new HashSet<Persion>();
    hs1.add(new Persion(1));
    hs1.add(new Persion(2));
    System.out.println(hs1);
    }
    }
    应该能明白啦~
      

  16.   

    你说的不对,equals方法已经覆盖了,见源代码 16-20行
      

  17.   

    看第3句~ 你覆盖hashCode()了么~
      

  18.   


    我猜想是不是这个原因,,实际上,hashCode是第一道门槛,equals是第2道门槛,也就是说,在将元素添加到Set集合的过程中,是首先调用hashCode来判断,在hashCode返回相同的前提下,才调用equals来判断2个对象是否真实相等。而,我给的这段代码中,(虽然是个考试题故意设置的畸形代码),hashCode是沿用Object版本的,也就是比较内存地址,所以任何2个不同对象直接调用hashCode()方法就已经不相同了,也就是,在第一道门槛这里,运行时已经将他们当作不等对象了。根本无需等到第2道门槛利用equals来验证。打个比方来说,比如比较2个人首先看 名字是否相同,(中国同名的人太多了)【相当于hashCode方法】,在人名相同的前提下才进一步比较其他额外属性,比如爱好,血型,年龄『相当于equals方法』,正常情况下,同样的人和克隆人都能同时满足上述条件,但是,给出2人,只要他们名字不同,就直接认定是不同的人了,
      

  19.   


    我一开始就说了,这个程序没覆盖hashCode()但是覆盖了equals(),是一种畸形的程序
      

  20.   

    本题中:既然选择B,就是说,一个HashSet可以包含多个有相同name的Person对象,但是按照第16行---第20行,这些Person对象彼此调用equals(Object o) ,应该都返回true吧,那么不是和HashSet的唯一性矛盾了?? 
    HashSet是如何判断对象是否唯一呢,还不是这句:
    if (e.hash == hash && ((k = e.key) == key || key.equals(k)))
    2个限制,hashCode()与equals()
    你看过代码确定你真知道HashSet是如何确定唯一的么?
      

  21.   


    当然,对于JAVA 来说,&& 是按照短路运算进行的,如果e.hash ==hash都是false了,那么根本不用看 &&后面的东西,再复杂都没用。。所以,是先判断hashCode是否相同,相同的前提下才看后面的((k = e.key) == key || key.equals(k))
      

  22.   

    虽然是2个限制,但是优先第一个,,在第一个返回true前提下才看第2个。所以,我给出结论:“在将元素添加到Set集合的过程中,是首先调用hashCode来判断,在hashCode返回相同的前提下,才调用equals来判断2个对象是否真实相等”是这样么?
      

  23.   

    看这符号 && 这个逻辑与明白了, 就是难兄难弟,哪有倒霉另一个就跟着出事。
      

  24.   


    public class Person { 
     private String name; 
     public Person(String name) { 
     this.name = name; 
     } 
     public boolean equals(Object o) { 
     if(!(o instanceof Person) ) return false; 
     Person p = (Person) o; 
     System.out.println(p);
     return p.name.equals(this.name); 
     } 
    public static void main(String[] args ){
    Person a= new Person("kikalo");
    Person b= new Person("kikalo");
    Person c=(Person)a;
    a.equals(b);
    a.equals(c);

    }}
    感觉是不一样的啊