Java Set 接口常見的實現類以及集合遍歷方式

  摘記一下最近敲的關於集合方面的Demo,會陸續作成博客記錄下來的。算法

  此次摘記的是:安全

  • Collections基本的遍歷方式:
    • 傳統的 foreach(T t : Collections c){ ... }
    • Collections自帶的Lambda遍歷方式: Collections c.forEach(obj-> ...)
    • Iterator 對象進行遍歷
  • Set 接口的四種常見實現類:
    • HashSet 無序而且性能比TreeSet高效,適用於基本的添加、查詢元素的場合。
    • TreeSet 採用的二叉樹的數據結構,須要採用紅黑樹算法維護數據的排序,對Set的數據類型有要求(須要實現Comparable接口或者是在TreeSet構造的時候定義排序),性能較HashSet低效,比較適用於須要保持排序的場景。
    • LinkedHashSet 採用的鏈表維護插入元素的順序,其餘與HashSet無太大差別,性能較HashSet略低(鏈表開銷)
    • EnumSet 這四種常見Set實現類中最高效的,採用的是位向量的數據結構存儲元素(存儲高效、緊湊),要求存儲元素必須是一個Enum(約束大),適用於枚舉值執行批量操做的場景

這裏補充說明下,上述的四種Set都是非線程安全的,即須要使用代碼同步機制保證多線程訪問的安全性。想使用線程安全的Set,可採用_synchronizedSortedSet_(線程安全)。數據結構

  如下爲筆者學習時採用的Demo,筆者將一些心得記錄放在了Demo的註釋中,若是有dalao能夠指點下小弟,歡迎私信:xb1997love@gmail.com多線程

public class SetDemo {
	static SetDemo mine = new SetDemo();

	/**
	 * 這是通常的Lambda表達式遍歷集合
	 */
	public void LambdaEx() {
		Collection<Integer> i = new HashSet<Integer>();
		i.add(1);
		i.add(2);
		i.add(3);
		i.add(4);
		i.forEach(obj -> System.out.println(obj));
	}
	/**
	 * 這是Lambda遍歷Iterator對象
	 */
	public void LambdaIterator() {
		Collection<Integer> i = new HashSet<Integer>();
		i.add(1);
		i.add(2);
		i.add(3);
		i.add(4);
		Iterator<Integer> it = i.iterator();
		// 這是傳統的使用Iterator對象循環遍歷集合
		// while(it.hasNext()) {
		// int num = (Integer)it.next();
		// if(num == 3) {
		// it.remove();
		// }
		// System.out.println(num);
		// }
		// 利用Lambda表達式循環輸出對象
		it.forEachRemaining(obj -> System.out.println(obj));
	}
	/**
	 * 這個是用來顯示Java 8中的IntStream LongStream DoubleStream等流式接口
	 */
	public void IntLongDoubleStream() {
		IntStream is = IntStream.builder().add(1).add(13).add(20).add(18).build();
		Collection<Integer> c = new HashSet<Integer>();
		c.add(1);
		c.add(2);
		c.add(3);
		c.add(4);
		// // 下面的彙集方法的代碼每次只能被調用一,就是下面的max() min() 等只能同時調用一個
		// System.out.println("MAX: "+ is.max().getAsInt());
		// System.out.println("MIN: "+is.min().getAsInt());
		// // 這個是用於匹配is中的全部元素是否都知足所述的要求
		// System.out.println("is 中全部元素的平方是否大於20:"+is.allMatch(ele->ele*ele>20));

		// // 這是對is中全部的對象進行修改
		// IntStream newIs = is.map(ele->ele*2);
		// newIs.forEach(ele->System.out.println(ele));
		c.stream().filter(ele -> ele > 4);

	}
	/**
	 * TreeSet Demo代碼 TreeSet
	 * 會自動對插入的元素進行排序,假若數據是使用的自定義的類型,那麼那個類型須要實現Comparable接口(compareTo方法) TreeSet
	 * 實際上應該是一顆徹底二叉樹,而且對插入的數據進行修改的話可能會致使Set混亂,由於會出現無序而且沒法找到元素的狀況
	 * 讀出數據的時候會採用中序遍歷的方式進行讀出
	 */
	public void TreeSetDemo() {
		// 默認的,使用基本的數字類型的會按照遞增的順序排列
		TreeSet<M> tSet = new TreeSet<M>((o1, o2) -> {
			M m1 = (M) o1;
			M m2 = (M) o2;
			// 遞減的排序
			return m1.age > m2.age ? -1 : m1.age < m2.age ? 1 : 0;
		});
		// 插入三條數據
		tSet.add(new M(5));
		tSet.add(new M(-3));
		tSet.add(new M(9));
		System.out.println(tSet);
	}
	/**
	 * HashSet Demo代碼
	 * HashSet保存元素是無序且惟一的,若是企圖修改其中的元素會致使Set進入混亂的狀態,進而Set沒法識別對應的元素,沒法精肯定位元素
	 * 假若須要重寫equals方法切近最後也重寫hashCode方法,由於equals方法相等的對象,它們的hashCode也應該相同。而HashSet須要利用這兩個函數進行重複的判斷
	 */
	public void HashSetDemo() {
		Set<R> hSet = new HashSet<R>();
		hSet.add(new R(5));
		hSet.add(new R(-3));
		hSet.add(new R(9));
		hSet.add(new R(-2));
		hSet.add(new R(3));

		// 初始狀態hSet
		System.out.println(hSet);
		// 這裏能夠給所引發加上一個type
		Iterator<R> it = hSet.iterator();
		// 作了一個很危險的操做,將Set的第一個元素的值修改成-3
		R first = (R) it.next();
		first.count = -3;
		// 修改後狀態hSet
		System.out.println(hSet);
		System.out.println("hSet 是否包含-2的元素? " + hSet.contains(new R(-2)));
		// 試圖移除這個R(-3)對象
		hSet.remove(new R(-3));
		System.out.println(hSet);
		System.out.println("hSet 是否包含-3的元素? " + hSet.contains(new R(-3)));
		System.out.println("hSet 是否包含-2的元素? " + hSet.contains(new R(-2)));
	}
	/**
	 * LinkedHashSet相較於HashSet的不一樣是,LinkedHashSet會以鏈表的形式維護插入元素的順序
	 */
	public void LinkedHashSetDemo() {
		Set<String> lSet = new LinkedHashSet<String>();
		// 重點是觀察其插入是具備有序性的,即LinkedHashSet會維持元素插入的順序
		lSet.add("t1");
		lSet.add("t2");
		System.out.println(lSet);
		// 移除再從新插入,順序會改變
		lSet.remove("t1");
		lSet.add("t1");
		System.out.println(lSet);
	}
	/**
	 * EnumSet Demo代碼 EnumSet 採用的是位向量來存儲變量,進行批量操做(ContainsAll retainAll()方法)特別合適
	 * EnumSet 不容許插入null值
	 */
	public void EnumSetDemo() {
		// 以一個枚舉類型建立一個EnumSet,此時會以枚舉的全部值填充此EnumSet
		EnumSet<Season> eSet = EnumSet.allOf(Season.class);
		// 以一個枚舉類型建立一個空的EnumSet,這裏只是單純地指定了枚舉的類型
		EnumSet<Season> eSet2 = EnumSet.noneOf(Season.class);
		// 比較下兩種EnumSet的區別
		System.out.println(eSet);
		System.out.println(eSet2);
		// 試圖插入一個枚舉值
		eSet2.add(Season.SPRING);
		// 以指定的枚舉值建立EnumSet
		EnumSet<Season> eSet3 = EnumSet.of(Season.SPRING,Season.SUMMER);
		// 以枚舉類型中指定範圍的值建立EnumSet
		EnumSet<Season> eSet4 = EnumSet.range(Season.SPRING,Season.WINTER);
		// eSet5 + eSet4 -> Season中全部的枚舉值
		EnumSet<Season> eSet5 = EnumSet.complementOf(eSet4);
	}
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		// Lambda遍歷集合的方式
		// mine.LambdaIterator();
		// 使用Stream對象遍歷集合的方式
		// mine.IntLongDoubleStream();
		// 常見的三種Set接口實現類
		// mine.TreeSetDemo();
		// mine.HashSetDemo();
		// mine.LinkedHashSetDemo();
		// mine.EnumSetDemo();
	}
	/**
	 * 用於演示TreeSet的類,該類未實現Comparable接口,而是將比較的方式在構建TreeSet的時候以構造函數的形式注入了
	 * 
	 * @author Administrator
	 *
	 */
	class M {
		int age = 0;

		public M(int age) {
			this.age = age;
		}

		public String toString() {
			return String.format("M[ age: %d]", age);
		}
	}
	/**
	 * 用於演示HashSet的類,關鍵在於重寫equals和hashCode方法
	 * @author Administrator
	 *
	 */
	class R {
		int count;

		public R(int count) {
			this.count = count;
		}

		@Override
		public String toString() {
			// TODO Auto-generated method stub
			return String.format("R[count:%d]", count);
		}

		@Override
		public boolean equals(Object obj) {
			// TODO Auto-generated method stub
			if (this == obj) {
				return true;
			}
			if (obj != null && obj.getClass() == R.class) {
				R r = (R) obj;
				return this.count == r.count;
			}
			return false;
		}

		@Override
		public int hashCode() {
			// TODO Auto-generated method stub
			return this.count;
			// return super.hashCode();
		}
	}
	/**
	 * 用於演示EnumSet的類
	 * @author Administrator
	 *
	 */
	enum Season{
		SPRING,SUMMER,FAIL,WINTER
	}
}
相關文章
相關標籤/搜索