List和Set的区別
Collection
|--List
有序(存储顺序和取出顺序一致),可重复
|--Set
无序(存储顺序和取出顺序不一致),唯一
1) HashSet:它不保证 set 的迭代顺序;特别是它不保证该顺序恒久不变。
注意:虽然Set集合的元素无序,但是,作为集合来说,它肯定有它自己的存储顺序,
而你的顺序恰好和它的存储顺序一致,这代表不了有序,你可以多存储一些数据,就能看到效果。
2) LinkedHashSet:底层数据结构由哈希表和链表组成。
哈希表保证元素的唯一性。
链表保证元素有素。(存储和取出是一致)
3)TreeSet:TreeSet集合的特点:排序和唯一
能够对元素按照某种规则进行排序。
排序有两种方式
A:自然排序
B:比较器排序
通过观察TreeSet的add()方法,我们知道最终要看TreeMap的put()方法。
问题一:它們分別是如何保证元素的唯一性呢?
1)HashSet
通过查看add方法的源码,我们知道这个方法底层依赖 两个方法:hashCode()和equals()。
步骤:
首先比较哈希值
如果相同,继续走,比较地址值或者走equals()
如果不同, 就直接添加到集合中
按照方法的步骤来说:
先看hashCode()值是否相同
相同:继续走equals()方法
返回true: 说明元素重复,就不添加
返回false:说明元素不重复,就添加到集合
不同:就直接把元素添加到集合
如果类没有重写这两个方法,默认使用的Object()。一般来说不同相同。
而String类重写了hashCode()和equals()方法,所以,它就可以把内容相同的字符串去掉。只留下一个。
2)TreeSet
①如果new TreeSet的时候使用的是无参构造方法,底层实现如下
public TreeSet() {
this(new TreeMap<E,Object>());//调用本类中封装的另一个带参构造
}
//NavigableMap是一个接口,TreeMap是实现了NavigableMap接口的类
TreeSet(NavigableMap<E,Object> m) {
this.m = m;
}
private transient NavigableMap<E,Object> m;
//当调用add()方法的时候,实际是调用了TreeMap对象的put()方法
//put()通过compare()比较add()传进来的对象与集合中元素
public boolean add(E e) {
return m.put(e, PRESENT)==null;
}
当调用add()方法的时候,实际是真正的比较是依赖于元素的compareTo()方法,而这个方法是定义在 Comparable里面的。所以,你要想重写该方法,就必须是先 Comparable接口。这个接口表示的就是自然排序。
②如果使用的是带参构造方法,需要传进一个实现了comparator接口的具体类对象或者匿名内部类作为形参。
实现了comparator接口的具体类中重写了compare()方法,以compare()方法对元素进行判断是否相同。
public TreeSet(Comparator<? super E> comparator) {
this(new TreeMap<>(comparator));
}
举例代码:
MyComparator :
import java.util.Comparator;
public class MyComparator implements Comparator<Student> {
@Override
public int compare(Student s1, Student s2) {
// int num = this.name.length() - s.name.length();
// this -- s1
// s -- s2
// 姓名长度
int num = s1.getName().length() - s2.getName().length();
// 姓名内容
int num2 = num == 0 ? s1.getName().compareTo(s2.getName()) : num;
// 年龄
int num3 = num2 == 0 ? s1.getAge() - s2.getAge() : num2;
return num3;
}
}
Student:
public class Student {
private String name;
private int age;
public Student() {
super();
}
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;
}
}
main:
public class TreeSetDemo {
public static void main(String[] args) {
// 创建集合对象
// TreeSet<Student> ts = new TreeSet<Student>(); //自然排序
// public TreeSet(Comparator comparator) //比较器排序
// TreeSet<Student> ts = new TreeSet<Student>(new MyComparator());
// 如果一个方法的参数是接口,那么真正要的是接口的实现类的对象
// 而匿名内部类就可以实现这个东西
TreeSet<Student> ts = new TreeSet<Student>(new Comparator<Student>() {
@Override
public int compare(Student s1, Student s2) {
// 姓名长度
int num = s1.getName().length() - s2.getName().length();
// 姓名内容
int num2 = num == 0 ? s1.getName().compareTo(s2.getName())
: num;
// 年龄
int num3 = num2 == 0 ? s1.getAge() - s2.getAge() : num2;
return num3;
}
});
// 创建元素
Student s1 = new Student("linqingxia", 27);
Student s2 = new Student("zhangguorong", 29);
Student s3 = new Student("wanglihong", 23);
Student s4 = new Student("linqingxia", 27);
Student s5 = new Student("liushishi", 22);
Student s6 = new Student("wuqilong", 40);
Student s7 = new Student("fengqingy", 22);
Student s8 = new Student("linqingxia", 29);
// 添加元素
ts.add(s1);
ts.add(s2);
ts.add(s3);
ts.add(s4);
ts.add(s5);
ts.add(s6);
ts.add(s7);
ts.add(s8);
// 遍历
for (Student s : ts) {
System.out.println(s.getName() + "---" + s.getAge());
}
}
}