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());
}
}
}