Set
和List一样同属于Collection接口的子接口,但有区别:
Set:元素不允许重复
List:元素允许重复
Set的三个子类
HashSet
- 元素唯一且无序
HashSet之所以能保证唯一性,是靠重写HashCode方和和equals方法
重写HashCode是为了优化次数,减少调用equals的次数
package org.org.westos.demo;
import java.util.HashSet;
public class MyClass {
public static void main(String[] args) {
HashSet<String> set = new HashSet<>();
set.add("aaa");
set.add("bbb");
set.add("ccc");
set.add("ddd");
//添加两个重复元素
set.add("ddd");
set.add("ccc");
//新式for循环遍历
for (String s : set) {
System.out.println(s);
}
}
}
看得出来,输出的内容不会有重复,且输出的顺序与存储的顺序不一致,这就是HashSet集合的特点,另外,HashSet集合底层用的是HashMap集合来存储的,HashMap下一章会讲
LinkedHashSet
- 元素唯一且有序,底层数据结构哈希表保证元素唯一,链表保证元素有序
package org.org.westos.demo;
import java.util.LinkedHashSet;
public class MyClass {
public static void main(String[] args) {
LinkedHashSet<Integer> set = new LinkedHashSet<>();
set.add(100);
set.add(300);
set.add(400);
set.add(200);
//添加重复元素
set.add(100);
set.add(300);
set.add(400);
set.add(200);
//遍历集合
for (Integer integer : set) {
System.out.println(integer);
}
}
}
由运行结果可验证,LinkedHashSet集合输出的集合元素不会重复,且按照存储顺序输出
TreeSet
- 元素唯一且有序
package org.org.westos.demo;
import java.util.TreeSet;
public class MyClass {
public static void main(String[] args) {
TreeSet<Integer> set = new TreeSet<>();
set.add(11);
set.add(55);
set.add(33);
set.add(44);
set.add(22);
//添加重复元素
set.add(11);
set.add(55);
for (Integer integer : set) {
System.out.println(integer);
}
}
}
就目前元素唯一的特点,跟LinkedHashSet是一样的,但除此之外,TreeSet集合还有自己的特点,那就是可以对集合中的元素进行排序,这里已经对其采用了自然排序的方式
- 排序
如果实现按从小到大或者从大到小的顺序输出
根据方法返回值的正、负和0来决定此元素在红黑树中放置的左右顺序
如果是正,作在右子树,如果是负,作为左子树,如果是0就不往里放
然后在遍历集合时,以左中右的方式获取元素,这样,遍历出来的元素就是排好序的
(1)自然排序
如果使用空参构造,就采用自然排序
采用自然排序,就要求元素必须实现Comparable接口,并重写其中的compareTo方法
package org.org.westos.demo;
public class Student implements Comparable<Student>{
//比较方法
@Override
public int compareTo(Student student){
//根据年龄大小排序
int num = this.age-student.age;
//年龄一样不能说明是同一个对象,还得比较姓名
//如果不比较,集合会当作是同一个元素,根据唯一性就不会放入
int num2 = num==0?this.name.compareTo(student.name):num;
//返回比较结果,只看正、负、0
return num2;
}
}
package org.org.westos.demo;
import java.util.TreeSet;
public class MyClass {
public static void main(String[] args) {
TreeSet<Student> treeSet = new TreeSet<>();
treeSet.add(new Student("张三",33));
treeSet.add(new Student("李四",22));
treeSet.add(new Student("王五",11));
treeSet.add(new Student("赵六",55));
treeSet.add(new Student("田七",22));
//遍历集合
for (Student student : treeSet) {
System.out.println(student);
}
}
}
这就是自然排序,这里Student类里省略了学生姓名、年龄和重写toString方法,只说重点
(2)比较器排序
如果使用有参构造,就采用比较器排序
采用比较器排序,需要传入一个比较器Comparator(接口)实现该接口中的compareTo方法,一般采用匿名内部类传入,就不需要重新编写一个比较器实现接口了
package org.org.westos.demo;
import java.util.Comparator;
import java.util.TreeSet;
public class MyClass {
public static void main(String[] args) {
//使用匿名内部类传入比较器的子类对象
TreeSet<Student> treeSet = new TreeSet<>(new Comparator<Student>() {
@Override
public int compare(Student o1, Student o2) {
//这里比较姓名长度来排序
int num = o1.getName().length()-o2.getName().length();
//姓名长度一样,还要比较姓名内容是不是一样
int num2 = num==0?o1.getName().compareTo(o2.getName()):num;
//姓名内容也一样,就按照年龄排序
int num3 = num2==0?o1.getAge()-o2.getAge():num2;
return num3;
}
});
treeSet.add(new Student("张三gasrgasg",33));
treeSet.add(new Student("李四afgargs",22));
treeSet.add(new Student("王五gsrgasgsgd",11));
treeSet.add(new Student("赵六asgsa",55));
treeSet.add(new Student("田七gwrgarg",25));
//遍历集合
for (Student student : treeSet) {
System.out.println(student);
}
}
}
在实现排序的时候,按什么排序,主要看方法里的逻辑语句
比如这里举的例子都是按从小到大排序,如果要重大到小,只需给返回值前加一个“-”号
个人感受:随着接触的集合越来越多,很容易混乱,要完全清楚的认识每个集合之间的联系与区别,就是多使用它们,熟悉它们