首先要了解HashSet实现的机制,如果不了解,对于下面的操作也是一直半解,用过一次就忘,并没有什么意义。
HashSet的实现内部其实是一个HashMap,HashMap的实现就是根据key来进行Hash变换映射到index下,如果index相同,会根据equal来判断key是不是相同,不相同就在这个index使用链表存储,key相同就覆盖原来的值。这样就保证了key的唯一性。HashSet就是利用HashMap key的唯一性来实现集合内的数据不重复。这就是HashSet的原理了。
根据上面的HashSet的实现原理,Set里面的元素都是HashMap的Key,而在HashMap中,Key默认使用的是字符串,为什么不使用对象呢?是因为在HashMap中判断Key是否相等的关键在于equal,Object默认的equal实现是使用==来判断两个对象是否相等的,这样就会导致String和普通的Object有很大的不同。String可以使用==来判断两个字符串是否相等,但是对于普通对象来说==只是判断对象的引用是否相等。这样一来,目标就很明确了,要保存自定义的不重复对象,必须要重写对象的equal方法。
再根据一条很有意义的规约:
重写equal方法必须要重写hashCode方法。
这样就知道我们需要做什么了:重写equal方法和hashCode方法。
下面例子指定一个属性,自定义具有相同该属性的对象只能存储一次,实现了不重复存储。也就是去重。
package hashSetExample; import java.util.HashSet; import java.util.Set; /** * Created by liusxg on 2017/3/9. */ public class RemoveDuplicateObj { static class Test { private String name; private Integer age; public String getName() { return name; } public void setName(String name) { this.name = name; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } //重写equal @Override public boolean equals(Object var1) { if (!(var1 instanceof Test)) { return false; } Test testVal1 = (Test) var1; if (testVal1.name == null) { return false; } return this.name == testVal1.getName(); } //重写hashCode @Override public int hashCode() { return this.name.hashCode(); } //重写toString,为打印方便 @Override public String toString() { return "{"+this.name+","+this.getAge()+"}"; } } public static void main(String[] args) { //测试数据 Test test1 = new Test(); test1.setName("小明"); test1.setAge(10); Test test2 = new Test(); test2.setName("小红"); test2.setAge(20); Test test3 = new Test(); test3.setName("小明"); test3.setAge(30); //测试代码 Set<Test> testSet = new HashSet<Test>(); testSet.add(test1); testSet.add(test2); testSet.add(test3); System.out.println(testSet.size()); System.out.println(testSet.toString()); } }输出结果: 2 [{小明,10}, {小红,20}]
如果把equal和hashCode重写注释掉,输出结果为: 3 [{小红,20}, {小明,10}, {小明,30}]