1. 程式人生 > >Java原始碼分析——java.util工具包解析(二)——HashSet、TreeSet、LinkedHashSet類解析

Java原始碼分析——java.util工具包解析(二)——HashSet、TreeSet、LinkedHashSet類解析

    Set,即集合,與數學上的定義一樣,集合具有三個特點:

  1. 無序性:一個集合中,每個元素的地位都是相同的,元素之間是無序的。
  2. 互異性:一個集合中,任何兩個元素都認為是不相同的,即每個元素只能出現一次。
  3. 確定性:給定一個集合,任給一個元素,該元素或者屬於或者不屬於該集合,二者必居其一,不允許有模稜兩可的情況出現。

    但是Java中的集合並不是嚴格意義上的集合,其中的TreeSet集合類並不滿足,因為它是有序的,除了無序性這一點,Java中的集合都嚴格滿足其它兩點。
    在Java中將集合類的起源定義為Set介面,並由該介面擴展出其他具體的實現,它們之間的關係如下:
在這裡插入圖片描述

    SortedSet介面繼承自Set介面,在其內部定義了排序的行為交由TreeSet實現排序的集合。在AbstractSet抽象類中,與AbstractList類一樣,為其子類提供對其自身的操作,即集合類操作,只是集合的內部操作,並沒有實現集合間的操作。

HashSet

    HashSet類是滿足集合屬性的三個屬性的,插入到其內部的元素是有其hashcode值決定的,其內部的具體實現是由HashMap完成的,平時我們建立一個新的HashSet類,實際上是建立了一個HashMap來完成我們所需的操作,HashSet類的幾個構造方法如下:

	private transient HashMap<E,Object> map;
	
    public HashSet() {
        map = new HashMap<>();
    }

    public HashSet(Collection<? extends E> c) {
        map = new HashMap<>(Math.max((int) (c.size()/.75f) + 1, 16));
        addAll(c);
    }

    public HashSet(int initialCapacity,
float loadFactor) { map = new HashMap<>(initialCapacity, loadFactor); } public HashSet(int initialCapacity) { map = new HashMap<>(initialCapacity); }

    可見裡面都建立了HashMap物件,也就是說,HashSet是利用HashMap的不可重複性來完成集合的互斥性的,示例程式碼:

		String s1=new String("1");
        String s2=new String("1");
        String s3=new String("1");
        Set<String> set=new HashSet<>();
        set.add(s1);
        set.add(s2);
        set.add(s3);
        System.out.println(set.add(s1));//false
        Iterator iterable=set.iterator();
        while (iterable.hasNext()){
            System.out.println(iterable.next());//輸出:1
        }
        System.out.println(s1.hashCode()==s2.hashCode());//true

    HashMap是根據物件的hashcode值來判斷是否是同一個物件的,因為s1、s2、s3的值相同,所以它們的hashcode值也相同,在集合中加入s1與s2時加不進去。下面列出常用的方法:

	//獲取迭代器
	public Iterator<E> iterator() {
        return map.keySet().iterator();
    }
	//獲取元素的多少
    public int size() {
        return map.size();
    }
	//判斷是否為空
    public boolean isEmpty() {
        return map.isEmpty();
    }
	//判斷是否包含
    public boolean contains(Object o) {
        return map.containsKey(o);
    }
	//增加一個元素
    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();
    }

TreeSet

    TreeSet類是有序的集合類,它實現了SortedSet介面,但與HashSet類相同的是,它也是在內部委託給另外一個類來完成其功能的,這個類是TreeMap類,TreeMap類在其內部實現了紅黑樹,因此可以實現較快的排序:

 public TreeSet() {
        this(new TreeMap<E,Object>());
    }

    還可以直接返回第一個元素以及最後一個元素:

	public E first() {
        return m.firstKey();
    }
    public E last() {
        return m.lastKey();
    }

    加入到TreeMap中的元素必須實現Comparable與Comparator介面,用來進行比較,其示例程式碼如下:

		String s1=new String("1");
        String s2=new String("3");
        String s3=new String("2");
        Set<String> set=new TreeSet<>();
        set.add(s1);
        set.add(s2);
        set.add(s3);
        Iterator iterable=set.iterator();
        while (iterable.hasNext()) {
            System.out.println(iterable.next());
        }
  		//輸出為:1 2 3

LinkedHashSet

    LinkedHashSet繼承自HashSet,它不同於HashSet裡面的元素是無序的,它是按照元素的插入順序來儲存元素的,也就是說它是線性的,其內部是呼叫類父類的構造方法:

HashSet(int initialCapacity, float loadFactor, boolean dummy) {
        map = new LinkedHashMap<>(initialCapacity, loadFactor);
    }

    採用了LinkedHashMap來實現其功能,LinkedHashMap是線性的HashMap,所以LinkedHashSet即為線性的。示例程式碼:

String s1=new String("1");
        String s2=new String("32");
        String s3=new String("243242");
        Set<String> set=new HashSet<>();
        set.add(s1);
        set.add(s2);
        set.add(s3);
        Iterator iterable=set.iterator();
        while (iterable.hasNext()) {
            System.out.println(iterable.next());
        }
        //輸出:1 243242 32
        Set<String> set2=new LinkedHashSet<>();
        set2.add(s1);
        set2.add(s2);
        set2.add(s3);
        Iterator iterable2=set2.iterator();
        while (iterable2.hasNext()) {
            System.out.println(iterable2.next());
        }
        //輸出:1 32 243242

    總的來說,三者其實是Map類的一個封裝而已。