十一、Java集和(五)——Set接口

List接口——Collection子接口

1. 主要實現類和方法

1)主要實現類: HashSet
2) 常用方法:
Set接口中聲明的方法都是Collection接口聲明過的。HashSet能使用的就是Collection中定義的方法。

@Test
public void test1(){
	Set set = new HashSet();
	
	set.add(223);
	set.add(new String("AA"));
	set.add("CC");
	set.add(223);
	set.add(new String("AA"));
	set.add(new Person("Tom",12));
	set.add(new Person("Tom",12));
	set.add(null);
	
	for(Object obj : set){
		System.out.println(obj);
	}	
}
2. Set的特性:無序性、不可重複性

1) 無序性:
不等同於隨機性!元素在底層儲存的位置不是像數組一樣是依次緊密排列的,而是參考其hashCode值決定的存儲位置。無序指底層存儲無序。
2) 不可重複性:
根據對象的equals()進行判斷。如果返回true,則添加失敗。保證了不可重複性。

3. HashSet

1) HashSet源碼解析
底層採用HashMap存儲元素

public HashSet() {
    map = new HashMap<>();
}

key存放set的元素,value存放object對象

private static final Object PRESENT = new Object();

public boolean add(E e) {
    return map.put(e, PRESENT)==null;
}

public boolean remove(Object o) {
    return map.remove(o)==PRESENT;
}

public void clear() {
    map.clear();
}

2) 向HashSet中添加數據的過程 (HashSet底層使用數組+鏈表+(jdk8:紅黑樹)存儲)
在這裏插入圖片描述

  • ① 將元素e1添加到HashSet中,首先調用e1所在類的hashCode(),獲取e1對象的哈希值。

  • ② 此哈希值,經過某種算法以後,獲取其在HashSet底層數組中的存放位置。

  • ③ 如果此位置上,沒有其他任何元素,則e1添加成功 —>情況1
    如果此位置上,已經存在某個或某幾個元素e2,則繼續判斷。

  • ④ 比較e1和e2的哈希值,如果兩個哈希值不相同。則e1添加成功。 —>情況2
    比較e1和e2的哈希值,如果兩個哈希值相同,則調用e1所在類的equals()方法

  • ⑤ equals()方法返回false,則e1添加成功。 —>情況3
    equals()方法返回true,則e1添加失敗。
    在這裏插入圖片描述

3) 向HashSet中添加元素對添加的元素所屬的類的要求

  • 針對於HashSet或者LinkedHashSet來說,如果多個對象需要存儲到上述兩個Set中時,爲了保證不可重複性,以及根據對源碼的分析,必須要求對象所屬的類要重寫hashCode()和equals()。
  • 重寫hashCode()和equals()要保證一致性!相等的對象必須具有相等的散列碼(哈希值)

4) HashSet擴容機制

縱向和橫向的變化,如果一個位置的鏈表長度達到8,但是整個數組的數量還不到64,就對這個數組進行擴容;
如果這個數組已經超過64了,而且這個位置鏈表還超過8了,就會轉化爲樹
4. Set的不同實現類的對比
|-----Collection:存儲一個一個的數據
  		|-----Set:存儲無序的、不可重複的數據: 高中的集合
  			|-----HashSet:是Set的主要實現類;線程不安全的;可以存儲null值
				|-----LinkedHashSet:是HashSet的子類;在添加數據之外,
									還通過一對指針記錄先後添加的順序,
									使得遍歷Set元素時,較HashSet效率更高。
			
			|-----TreeSet:可以按照添加的元素的指定的屬性的大小進行遍歷;
							底層使用的是紅黑樹(排序二叉樹的一種)
5. LinkedHashSet的測試(瞭解)
public class LinkedHashSet<E>
    extends HashSet<E>
    implements Set<E>, Cloneable, java.io.Serializable {
public LinkedHashSet() {
    super(16, .75f, true);
}
HashSet(int initialCapacity, float loadFactor, boolean dummy) {
    map = new LinkedHashMap<>(initialCapacity, loadFactor);
}
 @Test
	public void test2(){
		Set set = new LinkedHashSet();
		
		set.add(223);
		set.add(new String("AA"));
		set.add("CC");
		set.add(223);
		set.add(new String("AA"));
		set.add(new Person("Tom",12));
		set.add(new Person("Tom",12));
		set.add(null);
		
		for(Object obj : set){
			System.out.println(obj);//輸出結果的順須和HashSet對比,應該是不同的。
		}
		
	}

在這裏插入圖片描述

6. TreeSet添加數據的情況 (瞭解)
1) TreeSet可以按照添加的元素的指定的屬性的大小進行遍歷;
2) 排序的方式有:① 自然排序 ② 定製排序
3) TreeSet底層使用的是紅黑樹(排序二叉樹的一種)
4) 要求:向TreeSet中添加的元素必須是同一個類型的對象。
5) 說明:TreeSet中不能存放相同的元素。
	判斷的標準不再是元素所在類的hashCode()和equals()了。
	而是按照自然排序或定製排序中重寫的compareTo()或compare()進行比較。
		如果compareTo或compare返回0,則代表相同,無法添加;
		如果返回正數,則代表兩個對象的前者比後者大;
		如果返回負數,則代表兩個對象的前者比後者小。

例子1
自然排序(通過元素所屬類實現的Comparable接口)

    @Test
	public void test1(){
		
		TreeSet set = new TreeSet();
		set.add("CC");
		set.add("MM");
		set.add("GG");
		set.add("TT");
		set.add("JJ");
		set.add("KK");
//		set.add(123);//報ClassCastException
		
		Iterator iterator = set.iterator();
		while(iterator.hasNext()){
			System.out.println(iterator.next());
		}
		
	}

輸出結果:

CC
GG
JJ
KK
MM
TT

例子2
自然排序

@Test
    public void test4(){
        TreeSet<Integer> integers = new TreeSet<>(com);
        integers.add(2);
        integers.add(22);
        integers.add(25);
        integers.add(12);
        integers.add(7);
        System.out.println(integers);
    }

輸出結果:

[2, 7, 12, 22, 25]

定製排序

@Test
    public void test4(){
        Comparator<Integer> com = new Comparator<Integer>() {
            @Override
            public int compare(Integer o1, Integer o2) {
                return -o1.compareTo(o2);
            }
        };
        TreeSet<Integer> integers = new TreeSet<>(com);
        integers.add(2);
        integers.add(22);
        integers.add(25);
        integers.add(12);
        integers.add(7);
        System.out.println(integers);
    }

輸出結果:

[25, 22, 12, 7, 2]

例子3

Person類的定義
public class Person implements Comparable{
	private String name;
	private int age;
	public Person() {
		super();
	}
	public Person(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;
	}
	@Override
	public String toString() {
		return "Person [name=" + name + ", age=" + age + "]";
	}
	
	*/
	//先按照年齡從小到大排列,再按照姓名從小到大排
	@Override
	public int compareTo(Object o) {
		if(o == null) return -1;
		if(o == this) return 1;
		if(o instanceof Person){
			Person p = (Person)o;
			int value = this.age - p.age;
			if(value != 0){
				return value;
			}else{
				return this.name.compareTo(p.name);
			}
		}
		
		throw new RuntimeException("輸入的類型不匹配");
	}
	
	
}

自然排序

@Test
	public void test2(){
		TreeSet set = new TreeSet();
		
		Person p1 = new Person("Tom",12);
		Person p2 = new Person("Jim",32);
		Person p3 = new Person("Jerry",26);
		Person p4 = new Person("Mike",43);
		Person p5 = new Person("Lily",26);
		
		set.add(p1);
		set.add(p2);
		set.add(p3);
		set.add(p4);
		set.add(p5);
		
		Iterator iterator = set.iterator();
		while(iterator.hasNext()){
			System.out.println(iterator.next());
		}
	}

定製排序:當Compareable接口和Comaprator接口都存在時,優先按照Comparator接口的規則排序

@Test
	public void test3(){
		Comparator com = new Comparator(){

			@Override
			public int compare(Object o1, Object o2) {//o1,o2應該爲Person的實例
				if(o1 instanceof Person && o2 instanceof Person){
					Person p1 = (Person)o1;
					Person p2 = (Person)o2;
					return -p1.getName().compareTo(p2.getName());
				}
				
				return 0;
			}
			
		};
		TreeSet set = new TreeSet(com);
		
		Person p1 = new Person("Tom",12);
		Person p2 = new Person("Jim",32);
		Person p3 = new Person("Jerry",26);
		Person p4 = new Person("Mike",43);
		Person p5 = new Person("Lily",26);
		
		set.add(p1);
		set.add(p2);
		set.add(p3);
		set.add(p4);
		set.add(p5);
		
		Iterator iterator = set.iterator();
		while(iterator.hasNext()){
			System.out.println(iterator.next());
		}
	}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章