1. 程式人生 > >Java集合框架:Set(HashSet,LinkedHashSet,TreeSet)

Java集合框架:Set(HashSet,LinkedHashSet,TreeSet)

Set概述

 Set幾乎都是內部用一個Map來實現, 因為Map裡的KeySet就是一個Set,而value是假值,全部使用同一個Object。Set的特徵也繼承了那些內部Map實現的特徵。

HashSet

1. 定義

package java.util;
public class HashSet<E>
    extends AbstractSet<E>
    implements Set<E>, Cloneable, java.io.Serializable
{
    private transient HashMap<E,Object> map;
    private static final Object PRESENT = new Object();
    public HashSet() {
        map = new HashMap<>();
    }
//其餘省略
}

2. 概述

 HashSet是基於HashMap來實現的,操作很簡單,更像是對HashMap做了一次“封裝”,而且只使用了HashMap的key來實現各種特性,而HashMap的value始終都是PRESENT。
HashSet不允許重複(HashMap的key不允許重複,如果出現重複就覆蓋),允許null值,非執行緒安全
 羅列幾個主要方法:

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

3. 使用

 案例1:

		Set<String> set = new HashSet<>();
        set.add("s2");
        set.add("s3");
        set.add("s4");
        set.add("s2");
        set.add("s5");
        set.add("s1");
        set.add(null);
        set.add("s21");
        set.add("sw2");
        set.add("s2");
        for(String i:set)
            System.out.println(i);
        System.out.println(set);

 輸出:

null
s2
s1
sw2
s21
s5
s3
s4
[null, s2, s1, sw2, s21, s5, s3, s4]

LinkedHashSet

1. 定義(整個LinkedHashSet的程式碼)

package java.util;
public class LinkedHashSet<E>
    extends HashSet<E>
    implements Set<E>, Cloneable, java.io.Serializable {
    private static final long serialVersionUID = -2851667679971038690L;

    public LinkedHashSet(int initialCapacity, float loadFactor) {
        super(initialCapacity, loadFactor, true);
    }

    public LinkedHashSet(int initialCapacity) {
        super(initialCapacity, .75f, true);
    }

    public LinkedHashSet() {
        super(16, .75f, true);
    }

    public LinkedHashSet(Collection<? extends E> c) {
        super(Math.max(2*c.size(), 11), .75f, true);
        addAll(c);
    }
}

2. 概述

 HashSet有4個共有的建構函式:

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

 還有一個包級私有的建構函式:

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

 這個建構函式就是LinkedHashSet的關鍵,整個LinkedHashSet的四個建構函式全部都是呼叫了HashSet中的這個包級私有的建構函式(建構函式中的dummy就是標記,無實用,為了實現方法的過載而已),也就是說LinkedHashSet就是基於LinkedHashMap實現的

3. 使用

案例2:

        Set<String> set = new LinkedHashSet<>();
        set.add("s2");
        set.add("s3");
        set.add("s4");
        set.add("s2");
        set.add("s5");
        set.add("s1");
        set.add(null);
        set.add("s21");
        set.add("sw2");
        set.add("s2");
        for(String i:set)
            System.out.println(i);
        System.out.println(set);

 輸出:

s2
s3
s4
s5
s1
null
s21
sw2
[s2, s3, s4, s5, s1, null, s21, sw2]

允許null值,保留插入順序,非執行緒安全。

TreeSet

1. 定義

package java.util;
public class TreeSet<E> extends AbstractSet<E>
    implements NavigableSet<E>, Cloneable, java.io.Serializable
{
    private transient NavigableMap<E,Object> m;
    private static final Object PRESENT = new Object();
    public TreeSet() {
        this(new TreeMap<E,Object>());
    }
    public TreeSet(Comparator<? super E> comparator) {
        this(new TreeMap<>(comparator));
    }
    public TreeSet(Collection<? extends E> c) {
        this();
        addAll(c);
    }
    public TreeSet(SortedSet<E> s) {
        this(s.comparator());
        addAll(s);
    }
//其餘省略
}

2. 概述

 TreeSet的內部基於TreeMap實現,同樣value永遠為PRESENT.
 案例3:

        Set<String> set = new TreeSet<String>();
        set.add("s2");
        set.add("s3");
        set.add("s4");
        set.add("s2");
        set.add("s5");
        set.add("s1");
//        set.add(null);
        set.add("s21");
        set.add("sw2");
        set.add("s2");
        for(String i:set)
            System.out.println(i);
        System.out.println(set);

 執行結果:

s1
s2
s21
s3
s4
s5
sw2
[s1, s2, s21, s3, s4, s5, sw2]

不允許重複,不允許null值(如果有基於null的比較器,就可以允許為null),預設按升序排列。
案例4(其中的Person2詳細見《Comparable與Comparator淺析》):

        Set<Person2> set = new TreeSet<Person2>(new Comparator<Person2>(){
            @Override
            public int compare(Person2 o1, Person2 o2)
            {
                if(o1==null || o2==null)
                    return 0;
                return o1.getAge()-o2.getAge();
            }
        });
        Person2 p1 = new Person2("zzh",18);
        Person2 p2 = new Person2("jj",17);
        Person2 p3 = new Person2("qq",19);
        Person2 p4 = new Person2(null,19);
        set.add(p1);
        set.add(p2);
        set.add(p3);
        set.add(p4);

        System.out.println(set);

 輸出結果:[jj:17, zzh:18, qq:19]
 可以看到不是強制性要求TreeSet的鍵為null。

參考資料:

歡迎支援《RabbitMQ實戰指南》以及關注微信公眾號:朱小廝的部落格。