Set是Collection的子接口,与List相对 Set集合中的元素的特点是1,无序性 2,无下标3,无重复的元素 Set是个接口,所以无法直接创建对象,要依赖它的实现类来创建对象 Set的实现类有两个,一个是HashSet,另一个是TreeSet
Set<String> set = new HashSet<>(); set.add("c"); set.add("b"); set.add("d"); set.add("a"); set.add("a"); System.out.println(set); //输出的结果是 [a, b, c, d]//根据输出结果,我们可以看出set 的无序性和无重复性set集合的功能与Collection集合的功能是一致的(就方法调用而言),下面我们看一下Set的实现类
1.HashSet HashSet是set的实现类,底层是由哈希算法实现的 因为HashSet中能会添加相同的对象,所以HashSet常常被用来去除集合中重复的元素
HashSet<String> hashSet = new HashSet<>(); hashSet.add("a"); hashSet.add("b"); hashSet.add("d"); hashSet.add("d"); System.out.println(hashSet); //输出的结果是 [a, b, d]根据输出的结果可以看出HashSet确实去除了重复的对象,那么add的底层实现具体是什么? 因为HashSet是由哈希算法来实现的,在一个对象要被添加到集合中的时候,会被系统分配一个哈希值,系统首先会根据这个对象的哈希值去和集合中的对象去比较,如果哈希值不同就可以加入,如果不同,就去比较俩字符串的值是否一样,如果一样就不能加入,因此把重复的对象是无法加入HashSet的
当我们加入的对象是数据类型不是系统的数据类型,我们来看一下HashSet如何去除重复的
HashSet<Student> hashSet = new HashSet<>(); hashSet.add(new Student("张三", 18)); hashSet.add(new Student("李四", age)); hashSet.add(new Student("张三", 18)); System.out.println(hashSet); //输出的结果是 [Student [name=张三, age=18], Student [name=张三, age=18], Student [name=李四, age=19]]可以看出,俩张三都被添加进去了,这时因为每个对象都在内存中开辟了一片空间,虽然俩对象中的元素相等,但是他们的地址是不一样的,所以系统认为他们是不同的,所以被添加进去了 如果我们要让系统把重复的去掉,我们只能从底层去操作,因为HashSet集合存储是先调用hashCode方法,这里我们将hashCode方法重写一下看看
//重写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]]这次可以看出重复的对象已经被去除了
HashSet是如何保证元素唯一性的呢? 是通过元素的两个方法:hashCode与equals方法来完成; 如果hashCode值相同,才会判断equals是否为true; 如果hashCode值不同,那么不会调用equals。 对于判断元素是否存在,以及删除等操作,依赖的方法同样是hashCode、equals方法
2.TreeSet(自然排序、数据结构二叉树、比较器排序) 2.1自然排序:
TreeSet<Integer> treeSet = new TreeSet<>(); treeSet.add(10); treeSet.add(9); treeSet.add(18); treeSet.add(11); treeSet.add(8); System.out.println(treeSet); //输出的结果是 [9, 10, 11, 18]//我们可以根据输出的结果看出TreeSet会将对象排序并将重复的删除了TreeSet的主要作用可以对set集合中元素进行排序String实现了Comparable接口,所以可以直接进行排序引用数据类型想要排序,必须实现Comparable接口,系统在add的时候,要添加的系统对象会调用这个比较方法,进行排序和去除重复的对象
注意:排序时,当主要条件相同时,一定要判断次要条件
2.2数据结构二叉树 可以对set集合进行排序,底层数据结构是二叉树; 保证元素唯一性的依据是,compareTo方法return 0
注意:TreeSet排序的第一种方式,让元素自身具有比较性; 元素需要实现Comparable接口,覆盖compareTo方法; 这种方式也被称为元素的自然顺序,或者叫做默认顺序
compareTo方法返回值为正数,返回值写死,那么就是怎么存进去怎么取出来。 compareTo方法返回值为负数数,返回值写死,那么就是先进后出
2.3比较器排序(TreeSet排序的第二种方式(当两种排序都存在时,比较器排序优先级更高)) 我们这里要用到接口Comparator中的比较方法,我们需要给Comparator接口写一个实现类,在类中重写抽象方法 compare(),方法中也是用来写排序的规则的
实现comparator接口
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; } }根据其实现类来为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]]这样即使类被装到很多个集合中,并且排序的条件不同,我们也可以为每一个集合创建一个特有的比较器来进行排序
所以 因此比较器用的多一些,在实际开发中,很多时候,引用数据类型是否具有比较性,或者比较规则,可能不由开发人员自己决定,
那么开发人员想要对应的引用数据类型按照自己的排序方式进行排列,那么就需要实现comparator接口,实现compare方法