天天看点

你是否真的会使用Set集合

        java中为我们提供了丰富的集合以供我们使用,最常用的集合有Collection接口下的List接口和Set接口,也有Map接口。这么多的集合帮助我们完成了很多java代码的操作过程。这里我们就java中的Set接口做一个简单的问题探讨。

        在Set接口下我们可以使用的实现类有很多,我们来讨论两个子实现类的使用。

        我们这边文章来讨论下Set接口中的不可重复性在不同的子实现类中的差异。

        我们通过阅读Set接口的api可以发现Set存在一个特点就是不可重复性。我们这里想要讨论的是在不同的Set子实现类中怎么定义重复。

        HashSet

            首先我们来看看HashSet,我对很初级程序员进行过面试,在面试的过程中,我经常的问道你会使用Set么,特性是什么。他们大多都能答出来,但是当我问不同的子实现类中如何定义重复,很少有人能知道。大多就跟我说一个对象放进入之后再放进去就是重复。那个内置根本原因是什么呢。简单说重复可以理解为相等。那么两个对象如何判断相等呢。判断相等的方式对于对象来讲可以有两种 使用 == 或者使用 equals 这两种方式 第一个要判断的是引用是否指向同一个对象 第二种方式如果我们没有进行任何重写 那么等同于第一种  但是我们可以根据我们自己相等的逻辑对equals方法进行重写。这样相等的逻辑就有可能发生变换。那么HashSet 到底用的是== 还是equals来判断呢。先说结论   HashSet  是通过equals 和 hashCode 两个来进行判断的 。 看下边的代码。

    package test;

       public class Person {

private int id;

private String name;

public int getId() {

return id;

}

public void setId(int id) {

this.id = id;

}

public String getName() {

return name;

}

public void setName(String name) {

this.name = name;

}

public Person(int id, String name) {

super();

this.id = id;

this.name = name;

}

package test;

import java.util.HashSet;

public class Test {

public static void main(String[] args) {

HashSet<Person> persons = new HashSet<Person>();

persons.add(new Person(1,"zs"));

persons.add(new Person(1,"zs"));

System.out.println(persons.size());

}

}

运行结果如下:

你是否真的会使用Set集合

       程序中我们两个Person对象的内容相同,一般在于我们业务需求中两个人我们就认为是同一个人,那么这个代码的运行结果就不符合我们的业务需求,我们希望如果两个对象的内容完全相同就是相同的对象也就是是重复的,不想再次添加到HashSet集合中去,那么我们就需要改变HashSet判断重复的条件,修改的方式就是重写我们的equlas 和 hashCode方法。我们在Person类中添加两个方法如下:

public boolean equals(Object obj) {

if (obj instanceof Person) {

Person p = (Person) obj;

if (this.id == p.id && this.name.equals(p.name)) {

return true;

} else {

return false;

}

} else {

return false;

}

}

public int hashCode() {

return this.id;

}

运行结果如下:

你是否真的会使用Set集合

        这个时候运行结果变成了1.因为两个对象的equals方法返回true并且hashCode值也相等,HashSet认为这是两个相同的对象,所以不允许添加。注意两个方法必须都进行重写,缺一不可。

这个时候我们再来说TreeSet集合。

        TreeSet集合和HashSet集合有一个不同点是TreeSet集合是一个有序集合,他的顺序叫做自然顺序,比如1,2,。。。。6

a,b,c.......z这都是我们的自然循序。但是我们的Person对象是没有自然循序的。这个怎么办呢,我们能不能将没有自然循序的Person插入到TreeSet集合中去呢,答案是可以的,但是如果直接插入运行就会报错,因为TreeSet要保证你插入的对象有序他做了一番约定,首先TreeSet集合中的对象需要实现Comparable接口并且重写其中的compareTo方法,这个compareTo方法就是TreeSet为我们对象进行排序的一个依据,我们看下面的代码

package test;

public class Person implements Comparable<Person>{

private int id;

private String name;

public int getId() {

return id;

}

public void setId(int id) {

this.id = id;

}

public String getName() {

return name;

}

public void setName(String name) {

this.name = name;

}

public Person(int id, String name) {

super();

this.id = id;

this.name = name;

}

@Override

public int hashCode() {

return this.id;

}

@Override

public boolean equals(Object obj) {

if (obj instanceof Person) {

Person p = (Person) obj;

if (this.id == p.id && this.name.equals(p.name)) {

return true;

} else {

return false;

}

} else {

return false;

}

}

@Override

public int compareTo(Person p) {

return this.id - p.id;

}

@Override

public String toString() {

return "Person [id=" + id + ", name=" + name + "]";

}

}

package test;

import java.util.HashSet;

public class Test {

public static void main(String[] args) {

TreeSet<Person> persons = new TreeSet<Person>();

persons.add(new Person(3,"zs"));

persons.add(new Person(2,"zs"));

persons.add(new Person(1,"zs"));

System.out.println(persons);

}

}

运行结果如下

你是否真的会使用Set集合

    通过观察我们发现:TreeSet通过id帮我们进行排序了,因为我们重写的compareTo方法对每一个对象的id数值进行了比对操作。所以TreeSet可以帮我们进行排序。如果id相减结果是负数这放在这个对象的前方位置,如果是正数那么继续跟后续的进行比较直到为负数或者最后一个。这个我们需要注意  如果this.id - p.id变成 p.id - this.id 顺序就变成倒序,这个感兴趣的同学可以去试试。

      这里要多说一句的是:如果一个HashSet中的数据就想存入一个没有实现Comparable接口的怎么办呢,这个时候我们要知道在TreeSet的构造方法中有一个构造方法是TreeSet(Comparator comparatro)的构造方法,需要传入一个比较器,方便中的对象进行比较,这里我们不太过度的赘述,看下面代码就应该懂得。

package test;

import java.util.TreeSet;

public class Test {

public static void main(String[] args) {

PersonComparator pc = new PersonComparator();

TreeSet<Person> persons = new TreeSet<Person>(pc);

persons.add(new Person(3,"zs"));

persons.add(new Person(2,"zs"));

persons.add(new Person(1,"zs"));

System.out.println(persons);

}

}

package test;

public class Person {

private int id;

private String name;

public int getId() {

return id;

}

public void setId(int id) {

this.id = id;

}

public String getName() {

return name;

}

public void setName(String name) {

this.name = name;

}

public Person(int id, String name) {

super();

this.id = id;

this.name = name;

}

@Override

public String toString() {

return "Person [id=" + id + ", name=" + name + "]";

}

}

package test;

import java.util.Comparator;

public class PersonComparator implements Comparator<Person>{

@Override

public int compare(Person p1, Person p2) {

return p1.getId() - p2.getId();

}

}

    那么我们了解了TreeSet的有序性之后也需要了解的是TreeSet的重复性是否和HashSet相同呢。答案是错误的。TreeSet中的重复性判断是根据compareTo的返回值做判断的  如果返回值=0就证明两个对象相等。比如我们修改主方法多添加一个id=1的Person,然后运行查看结果

你是否真的会使用Set集合

    发现运行结果中id=1的Person对象就一个,那是因为当我们添加第二个id=1的Person对象时发现和集合中其中一个的compareTo方法返回值为0,所以TreeSet认为他们是相同的就不再次添加