List集合去除重复对象及equals()、hashCode()方法的作用

时间:2022-01-16 16:45:04

        在java中,要将一个集合中重复的对象除去,如果这个集合中的数据类型是基本数据类型,可以直接将list集合转换成set,就会自动去除重复的元素,大家都知道set集合的特点就是没有重复的,这个就相对比较简单,这里不在详细说,我们下面说的list集合中的数据类型是一个对象类型的。

        当list集合中存储的类型是对象类型的时候,我们就不能简单的只把list集合转换成set集合就行了,这时我们需要在对象的实体类中去重写equals()方法和hashCode()方法,我们以一个list集合为例,在该例中,我们将People实体类中姓名和电话号码作为判断该对象重复的标识,在People的实体类中我们重写这两个方法如下:

public class People {

private String name;
private int id;
private String phoneNumber;
private int age;
private String introduce;

public People(String name, int id, String phoneNumber, int age,
String introduce) {
super();
this.name = name;
this.id = id;
this.phoneNumber = phoneNumber;
this.age = age;
this.introduce = introduce;
}
// ....... 这里省略getter和setter方法

@Override
public boolean equals(Object arg0) {
// TODO Auto-generated method stub
People p = (People) arg0;
return name.equals(p.name) && phoneNumber.equals(p.phoneNumber);
}

@Override
public int hashCode() {
// TODO Auto-generated method stub
String str = name + phoneNumber;
return str.hashCode();
}

}
        以上实体类中,我们在equals()方法中取出该对象的name和phoneNumber这两个属性值去判断比较,然后在重写的hashCode()方法中返回这两个属性值得hashCode值。
public class Test {		public static void main(String[] args) {					List<People> listPeople = new ArrayList<People>();		listPeople.add(new People("张三", 1, "13355556666", 23, "新员工"));		listPeople.add(new People("张三", 2, "15522223333", 23, "老员工"));		listPeople.add(new People("李四", 3, "13355556666", 23, "实习生"));		listPeople.add(new People("提莫", 4, "13311112222", 23, "经理"));		listPeople.add(new People("张三", 5, "13355556666", 23, "会计"));		listPeople.add(new People("德玛", 6, "3344", 23, "开发"));		listPeople.add(new People("卡特", 7, "13355556666", 23, "测试"));		listPeople.add(new People("提莫", 8, "13355556666", 23, "美工"));		listPeople.add(new People("提莫", 9, "13311112222", 23, "实施"));		listPeople.add(new People("卡兹克", 10, "13356786666", 23, "售前"));		listPeople.add(new People("亚索", 11, "13355556666", 23, "销售"));				Set<People> setData = new HashSet<People>();		setData.addAll(listPeople);				System.out.println("list- size----" + listPeople.size());		System.out.println("list-----" + listPeople.toString());				System.out.println("set- size----" + setData.size());		System.out.println("set-----" + setData.toString());				for(People pp : setData) {			System.out.println("p--" + pp.toString());		}			}		}
        运行这段代码之后,我们就会发现,在原来的list集合中姓名和电话号码都相同的对象就被会认为是重复的元素而删除掉,很明显运行结果已经达到我们的目的。

        这里需要说一下equals()方法和hashCode()方法,一般情况下我们重写equals()方法的时候都要去重写hashCode()方法,这是为什么呢?大家不妨可以这样去试一试上面那个例子,在实体类中将重写的hashCode()方法注释掉,再去运行该程序,这时就会发现运行结果并不是我们刚刚得到的结果,在set集合中,并没有将我们认为是重复的元素删除掉,下面我们通过这两个方法的源码去了解一下:

        String类中的equals()方法的源码如下

    public boolean equals(Object anObject) {
if (this == anObject) {
return true;
}
if (anObject instanceof String) {
String anotherString = (String)anObject;
int n = count;
if (n == anotherString.count) {
char v1[] = value;
char v2[] = anotherString.value;
int i = offset;
int j = anotherString.offset;
while (n-- != 0) {
if (v1[i++] != v2[j++])
return false;
}
return true;
}
}
return false;
}
        通过观察equals()方法的源码我们可以看出,该方法去比较两个对象时,首先先去判断两个对象是否具有相同的地址,如果是同一个对象的引用,则直接放回true;如果地址不一样,则证明不是引用同一个对象,接下来就是挨个去比较两个字符串对象的内容是否一致,完全相等返回true,否则false。

        String类中hashCode()方法的源码如下

    public int hashCode() {
int h = hash;
if (h == 0 && count > 0) {
int off = offset;
char val[] = value;
int len = count;

for (int i = 0; i < len; i++) {
h = 31*h + val[off++];
}
hash = h;
}
return h;
}
        以上是String类中重写的hashCode()方法,在Object类中的hashCode()方法是返回对象的32位JVM内存地址,也就是说如果我们不去重写该方法,将会返回该对象的32位JVM内存地址,以上我们测试的例子中,当注释掉重写的hashCode()方法时,这时默认返回对象的32JVM中的地址,两个不同的对象地址显然是不同的,我们在比较时,虽然通过重写的equals()方法比较出来name和phoneNumber值是相同的,但是默认的hashCode()方法返回的值他们并不是同一个对象,所以我们通常要将hashCode()方法与equals()方法一起重写,以维护hashCode方法的常规协定,该协定声明相等对象必须具有相等的哈希码。

        曾在网上一篇博客中看到过这样的解释,用白话说,通过hashCode判断对象是否放在同一个桶里,然后再通过equals方法去判断这个桶里的对象是不是相同的,这个比喻也挺形象的。

        关于hashCode方法的作用大家可以看看这篇博客,讲的很清楚:http://blog.csdn.net/fenglibing/article/details/8905007