HashSet实现了Set接口。在实际开发中HashSet是Set的实现类里使用频率最高的类。既然实现了Set接口,所以HashSet必然是不能放入重复元素的。
那么什么是“重复的元素”呢?
先看下面的例子
public static void main(String[] args) { HashSet set=new HashSet(); String str1=new String("hello"); String str2=new String("hello"); set.add(str1); set.add(str2); System.out.println(set); }
输出结果:
也就是说str1和str2被认为是重复的,接着看第二个例子
public class Person { public Person(String name) { this.name = name; } public String getName() { return name; } public void setName(String name) { this.name = name; } private String name; }
public static void main(String[] args) { HashSet set=new HashSet(); /* String str1=new String("hello"); String str2=new String("hello"); set.add(str1); set.add(str2);*/ Person p1=new Person("zhangsan"); Person p2=new Person("zhangsan"); set.add(p1); set.add(p2); System.out.println(set); }
如果按照第一个例子的那么这里应该只输出一个“zhangsan”的内存地址
实际结果,输出了两个对象的内存地址,也就是说往Hashset中添加两个元素成功了,被认为是不同的元素。下面看下源码:
/** * Adds the specified element to this set if it is not already present. * More formally, adds the specified element <tt>e</tt> to this set if * this set contains no element <tt>e2</tt> such that * <tt>(e==null ? e2==null : e.equals(e2))</tt>. * If this set already contains the element, the call leaves the set * unchanged and returns <tt>false</tt>. * * @param e element to be added to this set * @return <tt>true</tt> if this set did not already contain the specified * element */ public boolean add(E e) { return map.put(e, PRESENT)==null; }
官方的注释可以解释成:add方法会先调用插入对象的hashCode方法 比较hashCode值是否相同,如果相同则不插入;如果hashCode值不同,再调用插入对象的Equals方法,如果equals返回true则不插入,反之则插入。
根据官方的解释已经可以明白为什么两个例子看似相同,而结果却不同了 ,因为Person类和String类的equals、hashCode方法实现并不同。我们知道String类重写了 Object类的quals、hashCode方法,而Person类并没有重写也就继承了Object类的两个方法。
此篇主要讲解Hashset,关于Object类的源码就不看了,直接说下结论
Object的equals 方法
自反性 x.equals(x)应该返回true
对称性 如果x.equals(y)为真,那么 y.equals(x)为真
传递性 x.equals(y)为true,并且y.equals(z)为true ,x.equals(z)为true
一致性 如果x.equals(y)为真 ,那么第二次比较直到第n此比较都应为真,在x,y没有被修改的情况下
非空性 对于任意的非空引用x x.equals(null)应返回false
Object 类的hashCode方法
1.在一次 java应用程序的调用中,对于同一个对象调用多次hashCode方法,应该返回相同的值(前提是对象没有被改变)
2.对于两个对象来说如果使用equals方法比较返回true,那么两个对象的hasCode值一定是相同的
3.如果两个对象equals返回false,不要求两个hashCode 值一定不同
4.对于Object类来说,不同的Object对象的hashCode值是不同的(Object类的hashCode值表示的是对象的地址)
第二个例子中p1,p2为两个对象实例,必然内存地址是不同的,所以hashCode返回false,元素直接被插入进去。下面我们在Person类中重写hashCode方法和equals方法
@Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Person person = (Person) o; return Objects.equals(name, person.name); } @Override public int hashCode() { return Objects.hash(name); }
再次运行main方法,打印出的set中只有一个对象的内存地址
ps:欢迎讨论,指正 qq:29138386
原创,转载请声明,谢谢