HashSet保存自定义不重复对象

时间:2023-02-07 17:01:43

前言

首先要了解HashSet实现的机制,如果不了解,对于下面的操作也是一直半解,用过一次就忘,并没有什么意义。

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}]