學習集合Set

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

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章