积一时之跬步,臻千里之遥程。
TreeSet
特点
保证元素唯一,并且可以对元素进行排序(自然排序和比较器排序)。
注意:使用TreeSet集合进行元素的排序,对元素有要求,要求这个元素必须实现Compareable
接口,否则无法进行自然排序。
保证元素的唯一是靠compareTo方法的返回值来确定的,如果返回0,表示两个元素相等。则不重复存储。
TreeSet的底层数据结构是二叉树,先存入一个树根,存储一个元素的时候,先和树根进行比较,比树根小
放在左边,大的放在右边。如果相等就不存储。取的时候按照左中右的顺序来取,所以默认是从小到大的。
代码演示(用TreeSet存储Integer类型的顺序再遍历)
public class Demo1 {
public static void main(String[] args) {
TreeSet<Integer> integers = new TreeSet<>();
integers.add(23);
integers.add(2);
integers.add(230);
integers.add(2323);
integers.add(223);
integers.add(233);
integers.add(123);
for (Integer integer : integers) {
System.out.print(integer+" ");
}
}
}
结果为 2 23 123 223 230 233 2323 可见自动排序
接下来就实现一个学生类,根据名字排序,如果名字相同就根据年龄来排序。
首先定义一个学生类,只有名字和年龄。
public class Student {
private String name;
private int age;
public Student(){}
public Student(String name, int age){
this.name = name;
this.age = age;
}
public void show () {
System.out.println(name +"..."+age);
}
public String getName(){
return name;
}
public int getAge(){
return age;
}
}
接下来就实现Comparable接口,并且重写compareTo方法。
public class Demo {
public static void main(String[] args) {
TreeSet<Student> ts = new TreeSet<>(new Comparator<Student>() {
@Override
public int compare(Student s1, Student s2) {
int num1=s1.getName().compareTo(s2.getName());
int num2=(num1==0)?(s1.getAge()-s2.getAge()):num1;
return num2;
}
});
ts.add(new Student("张三", 20));
ts.add(new Student("李四", 19));
ts.add(new Student("张三", 20));
ts.add(new Student("JIM", 22));
for (Student s : ts) {
s.show();
}
}}
运行结果:
JIM...22
张三...20
李四...19
实际上存储了两个上,当运行后只有一个。所以Treeset,体现了它的唯一性和有序性。
HashSet
特点
能保证元素的唯一性,但是是无序的。
HashSet底层数据结构是哈希表,哈希表是一个元素为链表的数组,综合了数组和链表的优点。
但是在jdk1.7之后,当哈希表中的一个链表元素超过8的时候该链表就自动转化为二叉树。
唯一性:当向HashSet集合中存入一个元素时,HashSet会调用hashCode()方法来的到该对象的
hashCode值,然后根据hashCode值决定该对象在HashSet中的存储位置。
HashSet集合判断两个元素相等的标准:两个对象通过hashCode()比较相等的时候,就会用equals()方法
来比较的。
所以HashSet保证元素唯一性的靠重写哈是Code()和equals()方法来保证的,如果不重写则无法保证。
@Override
public int hashCode() {
return 0;
因为成员变量值影响了哈希值,所以我们把成员变量值相加即可
return this.name.hashCode() + this.age;
看下面
s1:name.hashCode()=40,age=30
s2:name.hashCode()=20,age=50
尽可能的区分,我们可以把它们随变乘以一些整数
return this.name.hashCode() + this.age * 15;
}
@Override
public boolean equals(Object obj) {
// System.out.println(this + "---" + obj);
if (this == obj) {
return true;
}
if (!(obj instanceof Student)) {
return false;
}
Student s = (Student) obj;
return this.name.equals(s.name) && this.age == s.age;
}
@Override
public String toString() {
return "Student [name=" + name + ", age=" + age + "]";
}
接下来,遍历HashSet,同样我们用上面的Student类来遍历。(Student代码上面已经给出)
public class Demo {
public static void main(String[] args) {
HashSet<Student> studentHashSet = new HashSet<>();
studentHashSet.add(new Student("张三",123));
studentHashSet.add(new Student("李四",13));
studentHashSet.add(new Student("王老五",223));
for (Student student : studentHashSet) {
System.out.println(student.getName()+"+"+student.getAge());
}
}
}
结果
张三+123
李四+13
王老五+223
LinkedHashSet
特点
数据结构 链表和哈希表
链表保证有序,哈希表保证元素唯一
所以LinkedHashSet的特点就是,元素有序且唯一。但是线程不安全,效率高。
public class Demo {
public static void main(String[] args) {
LinkedHashSet<Integer> linkedHashSet = new LinkedHashSet<>();
linkedHashSet.add(10);
linkedHashSet.add(20);
linkedHashSet.add(30);
linkedHashSet.add(40);
linkedHashSet.add(10);
for (Integer integer : linkedHashSet) {
System.out.println(integer);
}
}
}
结果
10 20 30 40