Set是Collection的子接口,与List相对
Set集合中的元素的特色是1,无序性 2,无下标3,无重复的元素
Set是个接口,因此没法直接建立对象,要依赖它的实现类来建立对象
Set的实现类有两个,一个是HashSet,另外一个是TreeSetcss
Set<String> set = new HashSet<>(); set.add("c"); set.add("b"); set.add("d"); set.add("a"); set.add("a"); System.out.println(set);
输出的结果是java
[a, b, c, d]//根据输出结果,咱们能够看出set 的无序性和无重复性
Set的成员方法基本上都是继承了collection方法
下面咱们看一下Set的实现类算法
HashSet
HashSet是set的实现类,底层是由哈希算法实现的
由于HashSet中能会添加相同的对象,因此HashSet经常被用来去除集合中重复的元素sql
HashSet<String> hashSet = new HashSet<>(); hashSet.add("a"); hashSet.add("b"); hashSet.add("d"); hashSet.add("d"); System.out.println(hashSet);
输出结果是:json
[a, b, d]
根据输出的结果能够看出HashSet确实去除了重复的对象,那么add的底层实现具体是什么?
由于HashSet是由哈希算法来实现的,在一个对象要被添加到集合中的时候,会被系统分配一个哈希值,系统首先会根据这个对象的哈希值去和集合中的对象去比较,若是哈希值不一样就能够加入,若是不一样,就去比较俩字符串的值是否同样,若是同样就不能加入,所以把重复的对象是没法加入HashSet的bash
当咱们加入的对象是数据类型不是系统的数据类型,咱们来看一下HashSet如何去除重复的
先写一个StudentL类,student有俩变量,一个是名字.一个是年龄markdown
public class Student { private String name; private int age; public Student(String name, int age) { super(); this.name = name; this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } @Override public String toString() { return "Student [name=" + name + ", age=" + age + "]"; } }
下面咱们在HashSet集合中添加三个Student类的对象ide
HashSet<Student> hashSet = new HashSet<>(); hashSet.add(new Student("张三", 18)); hashSet.add(new Student("李四", age)); hashSet.add(new Student("张三", 18)); System.out.println(hashSet);
输出的结果是:post
[Student [name=张三, age=18], Student [name=张三, age=18], Student [name=李四, age=19]]
能够看出,俩张三都被添加进去了,可是重复的咱们并不想添加进去,为何会被添加进去呢
这时由于在建立student的时候,咱们给每一个对象都在堆内存中开辟了一片空间,虽然俩对象中的元素相等,可是他们的地址是不同的,因此系统认为他们是不一样的,因此被添加进去了
若是咱们要让系统把student中重复的去掉,咱们只能从底层去操做,由于HashSet是系统内部根据比较Hash值来判断的,而后在调用equals方法来比较的,因此咱们要重写Object类中的hashCode和equals方法
由于咱们要对Student类的对象排序,天然是Student类的对象来调用,因此在Student类中进行方法的重写flex
//重写hashCode方法 @Override public int hashCode() //这里返回的是要添加的对象的哈希值 { return 1; } //重写equals方法 @Override public boolean equals(Object obj) { Student student = (Student)obj; if (student.name.equals(this.name)&&student.age == this.age) { return true; } return false; } }
咱们再次运行,输出的结果是:
[Student [name=张三, age=18], Student [name=李四, age=19]]
此次能够看出重复的对象已经被去除了.
TreeSet
TreeSet是Set的一个实现类,底层是由二叉树实现的
下面咱们建立一个TreeSet类型的集合,并将几个整数装进去
TreeSet<Integer> treeSet = new TreeSet<>(); treeSet.add(10); treeSet.add(8); treeSet.add(15); treeSet.add(11); treeSet.add(8); System.out.println(treeSet);
输出的结果是
[8, 10, 11, 15]//咱们能够根据输出的结果看出TreeSet会将对象排序并将重复的删除了
HashSet的做用就是去除重复的对象,而TreeSet的主要做用就是排序
下面咱们在TreeSet中添加非系统类的对象
例:在TreeSet中添加四个学生类对象
TreeSet<Student> treeSet = new TreeSet<>(); treeSet.add(new Student("张三", 18)); treeSet.add(new Student("李四", 19)); treeSet.add(new Student("张三", 18)); System.out.println(treeSet);
当咱们运行的时候,会发现系统会报错,这是由于咱们添加进去的Student类,不是系统类,系统不知道怎么排序,因此会报错
咱们写系统类之因此系统会排序,是由于系统类中都实现了Comparable接口中的比较的抽象方法,系统在add的时候,要添加的系统对象会调用这个比较方法,进行排序和去除重复的对象
因此咱们须要在Student类中实现接口,重写接口中的比较方法
//重写comparable接口中的抽象方法,主要是把对象的排序的规则写进去 @Override //要注意的是当返回值为0的时候,系统就会认为俩值是同样的,就不会添加该对象 public int compareTo(Student o) { // TODO Auto-generated method stub int ageNum = this.age - o.age; int nameNum = this.name.compareTo(o.name); //当年龄同样的时候,咱们就比较俩者的姓名 return ageNum == 0 ? nameNum :ageNum; }
运行获得的结果是:
[Student [name=张三, age=18], Student [name=李四, age=19]]
能够看出咱们的运行结果已经根据年龄排序了,可是这种方法有一个局限,若是该类的对象被装到俩集合中,排序的条件不一样,这样的话就无法重写抽象方法了.
这时候咱们能够建立每个集合建立一个比较器,用比较器来排序
咱们这里要用到接口Comparator中的比较方法,咱们须要给Comparator接口写一个实现类,在类中重写抽象方法 compare(),方法中也是用来写排序的规则的
public class StudentImpl implements Comparator<Student> { @Override public int compare(Student o1,Student o2) { int intNum = o1.getAge() - o2.getAge(); int nameNum = o1.getName().compareTo(o2.getName()); return nameNum == 0 ? intNum :nameNum; } }
在实现了comparator接口后,咱们根据其实现类来为TreeSet建立一个比较器
TreeSet<Student> treeSet = new TreeSet<>(new StudentImpl()); treeSet.add(new Student("张三", 18)); treeSet.add(new Student("李四", 19)); treeSet.add(new Student("张三", 18)); System.out.println(treeSet);
这样输出的结果是:
[Student [name=张三, age=18], Student [name=李四, age=19]]
这样即便student类被装到不少个集合中,而且排序的条件不一样,咱们也能够为每个集合建立一个特有的比较器来比较