Java 集合

Java 集合

標籤 : Java基礎html


集合/容器

Java集合由Collection Map兩個接口派生而出,Collection表明序列式容器,Map表明關聯式容器.java


Collection

Collection做爲List Queue Set等序列式容器的父接口, 提供了一些公共基礎方法:mysql

  • update相關方法:
    boolean add(E e)
    boolean addAll(Collection<? extends E> c)
    void clear()
    boolean remove(Object o)
    boolean removeAll(Collection<?> c)
    boolean retainAll(Collection<?> c)(取交集)算法

  • select相關方法
    boolean contains(Object o)
    boolean containsAll(Collection<?> c)
    Iterator<E> iterator()
    Object[] toArray()
    <T> T[] toArray(T[] a)
    boolean isEmpty()
    int size() sql

詳細可參考JDK文檔編程


Iterator

iterator()方法返回一個迭代器Iterator.與其餘容器主要用於存儲數據不一樣,Iterator主要用於遍歷容器.
Iterator隱藏了各種容器的底層實現細節,嚮應用程序提供了一個遍歷容器的統一接口:api

方法 釋義
boolean hasNext() Returns true if the iteration has more elements.
E next() Returns the next element in the iteration.
void remove() Removes from the underlying collection the last element returned by this iterator (optional operation).

注意: 當遍歷Collection時不要使用Collection自帶的remove方法刪除數據,確實須要刪除時,須要使用Iterator提供的remove.數組

/** * @author jifang * @since 16/1/25 上午9:59. */
public class RemoveClient {

    Collection<Integer> collection = new ArrayList<>();

    @Before
    public void setUp() {
        Random random = new Random();
        for (int i = 0; i < 10; ++i) {
            collection.add(random.nextInt(i + 1));
        }
    }

    @Test
    public void client() {
        System.out.print("before:");
        for (Iterator<Integer> iterator = collection.iterator(); iterator.hasNext(); ) {
            Integer integer = iterator.next();
            System.out.printf(" %d", integer);
            if (integer == 0) {
                //collection.remove(i);
                iterator.remove();
            }
        }
        System.out.printf("%n after:");
        for (Integer integer : collection) {
            System.out.printf(" %d", integer);
        }
    }
}

Java 1.5提供foreach循環使得代碼更加簡潔,但實際foreach迭代容器元素底層也是用的Iterator,這一點能夠在調試時看得很清楚.markdown


List

List表明有序/可重複集合,所以ListCollection的基礎上添加了根據索引來操做元素的方法:數據結構

方法 描述
void add(int index, E element) Inserts the specified element at the specified position in this list (optional operation).
E get(int index) Returns the element at the specified position in this list.
int indexOf(Object o) Returns the index of the first occurrence of the specified element in this list, or -1 if this list does not contain the element.
int lastIndexOf(Object o) Returns the index of the last occurrence of the specified element in this list, or -1 if this list does not contain the element.
E remove(int index) Removes the element at the specified position in this list (optional operation).
E set(int index, E element) Replaces the element at the specified position in this list with the specified element (optional operation).
List<E> subList(int fromIndex, int toIndex) Returns a view of the portion of this list between the specified fromIndex, inclusive, and toIndex, exclusive.

List判斷兩個元素是否相等是經過equals()方法.

public int indexOf(Object o) {
    if (o == null) {
        for (int i = 0; i < size; i++)
            if (elementData[i]==null)
                return i;
    } else {
        for (int i = 0; i < size; i++)
            if (o.equals(elementData[i]))
                return i;
    }
    return -1;
}

ListIterator

List增長了返回ListIterator的方法:

方法 描述
ListIterator<E> listIterator() Returns a list iterator over the elements in this list (in proper sequence).
ListIterator<E> listIterator(int index) Returns a list iterator over the elements in this list (in proper sequence), starting at the specified position in the list.

ListIterator繼承自Iterator,專門用於操做List, 在Iterator的基礎上增長了以下方法:

方法 描述
void add(E e) Inserts the specified element into the list (optional operation).
void set(E e) Replaces the last element returned by next() or previous() with the specified element (optional operation).
boolean hasPrevious() Returns true if this list iterator has more elements when traversing the list in the reverse direction.
E previous() Returns the previous element in the list and moves the cursor position backwards.
int previousIndex() Returns the index of the element that would be returned by a subsequent call to previous().
int nextIndex() Returns the index of the element that would be returned by a subsequent call to next().

Iterator相比增長了前向迭代 獲取迭代元素index 以及add set的功能.


ArrayList

ArrayListList基於數組的實現,它封裝了一個動態自增加/容許再分配的Object[]數組:

/**
 * The array buffer into which the elements of the ArrayList are stored.
 * The capacity of the ArrayList is the length of this array buffer. Any
 * empty ArrayList with elementData == EMPTY_ELEMENTDATA will be expanded to
 * DEFAULT_CAPACITY when the first element is added.
 */
private transient Object[] elementData;

ArrayList可使用initialCapacity參數來設置該數組的初始長度ArrayList(int initialCapacity),或者使用默認長度DEFAULT_CAPACITY = 10; 當添加元素超過elementData數組容量時,ArrayList會從新分配數組, 以容納新元素:

/** * Appends the specified element to the end of this list. * * @param e element to be appended to this list * @return <tt>true</tt> (as specified by {@link Collection#add}) */
    public boolean add(E e) {
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        elementData[size++] = e;
        return true;
    }
private void ensureCapacityInternal(int minCapacity) {
        if (elementData == EMPTY_ELEMENTDATA) {
            minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
        }

        ensureExplicitCapacity(minCapacity);
    }

    private void ensureExplicitCapacity(int minCapacity) {
        modCount++;

        // overflow-conscious code
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }

    /** * Increases the capacity to ensure that it can hold at least the * number of elements specified by the minimum capacity argument. * * @param minCapacity the desired minimum capacity */
    private void grow(int minCapacity) {
        // overflow-conscious code
        int oldCapacity = elementData.length;
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity;
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            newCapacity = hugeCapacity(minCapacity);
        // minCapacity is usually close to size, so this is a win:
        elementData = Arrays.copyOf(elementData, newCapacity);
    }

若是在建立時就知道ArrayList的容量,最好同時指定initialCapacity的大小,以免從新分配數組,耗費性能.

ArrayList還提供以下方法來調整initialCapacity大小:

方法 描述
void ensureCapacity(int minCapacity) Increases the capacity of this ArrayList instance, if necessary, to ensure that it can hold at least the number of elements specified by the minimum capacity argument.
void trimToSize() Trims the capacity of this ArrayList instance to be the list’s current size.

工具類Arrays還提供了一個static方法List<T> asList(T... a), 該方法能夠把數組N個對象轉換成一個List集合, 這個List集合並非普通的ArrayList,而是Arrays內部實現的一個Arrays.ArrayList(一個固定長度的List,不可對該集合作add/remove操做).
關於ArrayList實現原理還能夠參考ArrayList源碼解析


LinkedList

LinkedList是基於雙向鏈表實現的List,雖然能夠根據索引來訪問集合中的元素,但性能不高(平均時間複雜度爲O(N)),但其插入/刪除操做很是迅速(尤爲是在頭尾,平均時間複雜度爲O(1));除此以外,LinkedList還實現了Deque接口,所以還能夠當成[雙端]隊列/棧來使用.
關於LinkedList的實現原理還能夠參考 [1.LinkedList源碼解析, 2. 雙向循環鏈表的設計與實現]

/** * @author jifang * @since 16/1/23 下午9:07. */
public class ListClient {

    private Random random = new Random();

    @Test
    public void client() {
        List<Integer> list = new LinkedList<>();
        for (int i = 0; i < 10; ++i) {
            list.add(random.nextInt(i + 1));
        }

        for (ListIterator<Integer> i = list.listIterator(); i.hasNext(); ) {
            if (i.next() == 0) {
                i.set(188);
                i.add(-1);
            }
        }

        System.out.println(list);
    }
}

Queue

Queue用於模擬隊列,隊列是一種先進先出/FIFO容器,新元素插到隊尾(offer), 訪問操做會返回隊首元素(poll). 一般, 隊列不容許隨機訪問隊列中的元素:

方法 描述
boolean add(E e) Inserts the specified element into this queue if it is possible to do so immediately without violating capacity restrictions, returning true upon success and throwing an IllegalStateException if no space is currently available.
boolean offer(E e) Inserts the specified element into this queue if it is possible to do so immediately without violating capacity restrictions.
E element() Retrieves, but does not remove, the head of this queue.
E peek() Retrieves, but does not remove, the head of this queue, or returns null if this queue is empty.
E poll() Retrieves and removes the head of this queue, or returns null if this queue is empty.
E remove() Retrieves and removes the head of this queue.

Queue有一個PriorityQueue實現類,另外Queue還有一個Deque子接口,表明能夠從兩端存取數據的隊列(所以Deque能夠當成Stack使用),Java爲Deque提供了ArrayDequeLinkedList兩個實現類.


PriorityQueue

PriorityQueue並非按照插入隊列順序進行排序,而是按照隊列元素的大小(權重)進行排序, 所以element/peek/poll/remove返回的並非最先進入隊列的元素,而是隊列中[權重]最小的元素:

/** * @author jifang * @since 16/1/28 下午6:20. */
public class QueueClient {

    @Test
    public void clientPriorityQueue() {

        Random random = new Random();

        Queue<Integer> queue = new PriorityQueue<>();
        for (int i = 0; i < 10; ++i) {
            queue.add(random.nextInt(100));
        }

        // 無序
        System.out.print("iterator:");
        for (Integer i : queue) {
            System.out.printf(" %d", i);
        }
        System.out.println();

        // 有序
        System.out.print("pool:");
        while (!queue.isEmpty()) {
            System.out.printf(" %d", queue.poll());
        }
        System.out.println();
    }
}

能夠看到遍歷PriorityQueue獲得的並非有序序列, 由於PriorityQueue內部並非一個按照順序排序的數組, 而是一個二叉堆(詳細能夠參考[1. PriorityQueue源碼解析, 2. 堆與堆排序 ]).

因爲須要排序,PriorityQueue不容許插入null;

PriorityQueue的元素有兩種排序方式

  • 天然排序: 採用天然排序的元素必須實現了Comparable接口.
  • 定製排序: 建立PriorityQueue時,傳入一個Comparator實例,該對象負責對元素進行排序,採用定製排序時不要求隊列元素實現Comparable接口.

關於兩種排序的詳細內容能夠參考下面關於TreeMap的討論.


Deque-ArrayDeque

Deque接口表明一個雙端隊列,提供了以下方法從隊列的兩端存取數據:

Java爲Deque提供了兩個實現類ArrayDeque(基於數組)與LinkedList(基於鏈表);因爲ArrayDeque底層基於數組E[] elements實現,所以建立時能夠指定一個numElements參數設置elements數組初始長度,若是不指定numElements參數,默認數組長度爲16(關於ArrayDeque的實現原理可參考ArrayDeque源碼解析).

Deque還能夠做爲棧stack使用, 他提供了以下方法:

@Test
public void asStack() {
    Deque<Integer> stack = new ArrayDeque<>();

    for (int i = 0; i < 10; ++i) {
        stack.push(i);
    }

    while (!stack.isEmpty()) {
        System.out.println(stack.pop());
    }
}

此外, LinkedList也實現了Deque接口,所以也能夠做爲Queue/Deque的實現類.


Map

Map用於保存具備映射關係的key-value數據,keyvalue之間存在單向一對一關係,經過指定的key,總能找到惟一肯定的value.

  • update相關
    V put(K key, V value)
    void putAll(Map<? extends K,? extends V> m)
    V remove(Object key)
    void clear()

  • select相關
    V get(Object key)
    Set<K> keySet()
    Collection<V> values()
    Set<Map.Entry<K,V>> entrySet()
    boolean containsKey(Object key)
    boolean containsValue(Object value)
    boolean isEmpty()
    int size()

Map內部定義一個Map.Entry<K,V>接口,封裝key-value對,Entry提供以下方法:

方法 描述
K getKey() Returns the key corresponding to this entry.
V getValue() Returns the value corresponding to this entry.
V setValue(V value) Replaces the value corresponding to this entry with the specified value (optional operation).

HashMap

HashMap是基於hash算法的Map實現(用它代替Hashtable),針對key-value的插入/檢索,這種形式具備最穩定的性能(O(1)),還可經過構造器對這一性能進行調整.
爲了成功在HashMap中存取數據,key對象必須實現hashCode()equals()方法,HashMap先經過keyhashCode()定位到元素所在桶,若是兩個元素在同一個桶,再用equals()進行判斷是否相等.若是兩個對象的hashCode()相同,但equals()不一樣, 則將兩個對象放在同一個的不一樣鏈表位置(這樣會致使hash效率降低).若是兩個對象經過equals()返回true, 但這hashCode()不一樣,則很是有可能致使HashMap將這兩個對象分配在不一樣桶中,從而使這兩個對象都添加成功,這就與Map規則衝突了.(關於HashMap詳細原理能夠參考: [1. 哈希表的設計與實現, 2.HashMap源碼解析]).

建議: 若是兩個對象經過equals()方法比較返回true, 則兩個對象的hashCode()值也相同.

hashCode()重寫規則:

  • 運行過程當中, 同一個對象屢次調用hashCode()應具備相同返回值;
  • 當兩個對象經過equals()比較返回true時, hashCode()應具備相同返回值;
  • 對象中用做equals()比較標準的實例變量, 都應該用於計算hashCode().
hashCode()重寫方法:
將每一個有意義的實例變量都計算出一個 inthashcode值.
類型 計算方式
boolean hashCode = (true ? 1 : 0);
float hashCode = Float.floatToIntBits(f);
double long value = Double.doubleToLongBits(f);
hashCode = (int)(value^(value>>>32));
int/short/byte hashCode = (int)i;
long hashCode = (int)(l^(l>>>32));
引用類型 hashCode = object.hashCode();
用上面計算出來的多個 hashcode組合計算成一個最終的 hashcode,爲了不直接相加產生偶然相等,能夠爲各個 hashcode乘以任意一個質數再相加:
  • String實現
public int hashCode() {
    int h = hash;
    if (h == 0 && value.length > 0) {
        char val[] = value;

        for (int i = 0; i < value.length; i++) {
            h = 31 * h + val[i];
        }
        hash = h;
    }
    return h;
}

StringhashCode()方法作了一些優化, 叫閃存散列碼, 詳見數據結構與算法分析 : Java語言描述

  • 自定義Bean
/** * @author jifang * @since 16/1/13下午7:50. */
public class Bean implements Serializable {

    private static final long serialVersionUID = 2975296536292876992L;

    private boolean isUsed;

    private double rate;

    private String name;

    @Override
    public int hashCode() {
        long rateHash = Double.doubleToLongBits(rate);
        int isUsedHash = isUsed ? 1 : 0;
        int nameHash = name.hashCode();

        return nameHash * 11 + (int) (rateHash ^ (rateHash >>> 32)) * 13 + isUsedHash;
    }

    // ..

}
  • HashMap的主要實現邏輯:
/**
 * Associates the specified value with the specified key in this map.
 * If the map previously contained a mapping for the key, the old
 * value is replaced.
 *
 * @param key key with which the specified value is to be associated
 * @param value value to be associated with the specified key
 * @return the previous value associated with <tt>key</tt>, or
 *         <tt>null</tt> if there was no mapping for <tt>key</tt>.
 *         (A <tt>null</tt> return can also indicate that the map
 *         previously associated <tt>null</tt> with <tt>key</tt>.)
 */
public V put(K key, V value) {
    if (table == EMPTY_TABLE) {
        inflateTable(threshold);
    }
    if (key == null)
 return putForNullKey(value);
    int hash = hash(key);
    int i = indexFor(hash, table.length);
    for (Entry<K,V> e = table[i]; e != null; e = e.next) {
        Object k;
        if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
            V oldValue = e.value;
            e.value = value;
            e.recordAccess(this);
 return oldValue;
        }
    }

    modCount++;
    addEntry(hash, key, value, i);
 return null;
}
/**
 * Adds a new entry with the specified key, value and hash code to
 * the specified bucket.  It is the responsibility of this
 * method to resize the table if appropriate.
 *
 * Subclass overrides this to alter the behavior of put method.
 */
void addEntry(int hash, K key, V value, int bucketIndex) {
    if ((size >= threshold) && (null != table[bucketIndex])) {
        resize(2 * table.length);
        hash = (null != key) ? hash(key) : 0;
        bucketIndex = indexFor(hash, table.length);
    }

    createEntry(hash, key, value, bucketIndex);
}

/**
 * Like addEntry except that this version is used when creating entries
 * as part of Map construction or "pseudo-construction" (cloning,
 * deserialization).  This version needn't worry about resizing the table.
 *
 * Subclass overrides this to alter the behavior of HashMap(Map),
 * clone, and readObject.
 */
void createEntry(int hash, K key, V value, int bucketIndex) {
    Entry<K,V> e = table[bucketIndex];
    table[bucketIndex] = new Entry<>(hash, key, value, e);
    size++;
}

注意

  • 當向Map類容器(如HashMap TreeMap 或後面的HashSet TreeSet)中添加可變對象時,必須十分當心,若是修改Map中的key,有可能致使該key與集合中的其餘key相等,從而致使沒法準確訪問該key-value.所以儘可能不要使用可變對象做爲Mapkey,或不要修改做爲key的對象(Setvalue於此類同)
  • Map還支持containsValue()方法來判斷一個value是否存在於Map中, 但該方法會遍歷全部的桶查找這個值, 所以性能較差, 不推薦使用
public boolean containsValue(Object value) {
    if (value == null)
        return containsNullValue();

    Entry[] tab = table;
    for (int i = 0; i < tab.length ; i++)
        for (Entry e = tab[i] ; e != null ; e = e.next)
            if (value.equals(e.value))
                return true;
    return false;
}

/** * Special-case code for containsValue with null argument */
private boolean containsNullValue() {
    Entry[] tab = table;
    for (int i = 0; i < tab.length ; i++)
        for (Entry e = tab[i] ; e != null ; e = e.next)
            if (e.value == null)
                return true;
    return false;
}

LinkedHashMap

LinkedHashMap使用雙向鏈表來維護key-value插入順序,所以性能略低於HashMap,但在須要順序迭代Map的場景下會有很是好的效率.

LinkedHashMap提供的addEntry()方法與HashMap有所不一樣,當使用LinkedHashMapput()時, 會從HashMap調回到LinkedHashMapaddEntry()方法,將新元素添加到鏈表尾:

/** * This override alters behavior of superclass put method. It causes newly * allocated entry to get inserted at the end of the linked list and * removes the eldest entry if appropriate. */
void addEntry(int hash, K key, V value, int bucketIndex) {
    super.addEntry(hash, key, value, bucketIndex);

    // Remove eldest entry if instructed
    Entry<K,V> eldest = header.after;
    if (removeEldestEntry(eldest)) {
        removeEntryForKey(eldest.key);
    }
}

/** * This override differs from addEntry in that it doesn't resize the * table or remove the eldest entry. */
void createEntry(int hash, K key, V value, int bucketIndex) {
    HashMap.Entry<K,V> old = table[bucketIndex];
    Entry<K,V> e = new Entry<>(hash, key, value, old);
    table[bucketIndex] = e;
    e.addBefore(header);
    size++;
}
  • 使用LinkedHashMap統計word出現次數
/** * @author jifang * @since 16/1/28 上午10:33. */
public class MapClient {

    private Random random = new Random();

    @Test
    public void clientLinkedHashMap() {
        Map<String, Integer> map = new LinkedHashMap<>();
        System.out.print("insert key:");
        for (int i = 0; i < 20; ++i) {
            String key = String.valueOf(random.nextInt(10));
            System.out.printf(" %s", key);
            if (map.get(key) == null) {
                map.put(key, 1);
            } else {
                map.put(key, map.get(key) + 1);
            }
        }
        System.out.printf("%n iterator:");

        for (Map.Entry<String, Integer> entry : map.entrySet()) {
            System.out.printf(" <%s -> %s>", entry.getKey(), entry.getValue());
        }

    }
}

WeakHashMap

WeakHashMapHashMap的區別在於:HashMapkey保留了對實際對象的強引用, 這意味着只要該HashMap不被銷燬,則Map的全部key所引用的對象不會被垃圾回收;但WeakHashMapkey只保留對實際對象的弱引用, 這意味着若是該key所引用的對象沒有被其餘強引用變量引用,則該對象可能被垃圾回收,WeakHashMap也會自動刪除這些key對應的key-value對.

@Test
public void clientWeakHashMap() {
    Map<String, String> map = new WeakHashMap<>();
    String key = "key";
    map.put(key, "value");
    map.put(new String("key1"), "value");
    map.put(new String("key2"), "value");
    System.out.printf("Before : %d%n", map.size());

    System.gc();
    System.runFinalization();
    System.out.printf("After : %d ", map.size());
}

若是使用WeakHashMap來保留對象的弱引用,則不要讓該key所引用的對象具備任何強引用, 不然將失去使用WeakHashMap的意義.


IdentityHashMap

HashMap不一樣,IdentityHashMap判斷元素是否相等的標準是用==而不是equals();

public boolean containsKey(Object key) {
    Object k = maskNull(key);
    Object[] tab = table;
    int len = tab.length;
    int i = hash(k, len);
    while (true) {
        Object item = tab[i];
        if (item == k)
 return true;
        if (item == null)
 return false;
        i = nextKeyIndex(i, len);
    }
}

SortedMap-TreeMap

Map接口派生出SortedMap接口表明根據key排序的key-value集合, TreeMap做爲SortedMap的實現類是一個紅黑樹結構,每一個key-value做爲紅黑樹的一個節點.TreeMap存儲key-value時,根據key值進行排序.所以TreeMap能夠保證全部元素都處於有序狀態,所以SortedMapMap的基礎上又添加了以下方法:

方法 描述
Comparator<? super K> comparator() Returns the comparator used to order the keys in this map, or null if this map uses the natural ordering of its keys.
K firstKey() Returns the first (lowest) key currently in this map.
K lastKey() Returns the last (highest) key currently in this map.
SortedMap<K,V> headMap(K toKey) Returns a view of the portion of this map whose keys are strictly less than toKey.
SortedMap<K,V> tailMap(K fromKey) Returns a view of the portion of this map whose keys are greater than or equal to fromKey.
SortedMap<K,V> subMap(K fromKey, K toKey) Returns a view of the portion of this map whose keys range from fromKey, inclusive, to toKey, exclusive.

TreeMap又在SortedMap的基礎上擴展了以下方法:

方法 描述
Map.Entry<K,V> ceilingEntry(K key) Returns a key-value mapping associated with the least key greater than or equal to the given key, or null if there is no such key.
K ceilingKey(K key) Returns the least key greater than or equal to the given key, or null if there is no such key.
Map.Entry<K,V> floorEntry(K key) Returns a key-value mapping associated with the greatest key less than or equal to the given key, or null if there is no such key.
K floorKey(K key) Returns the greatest key less than or equal to the given key, or null if there is no such key.
Map.Entry<K,V> higherEntry(K key) Returns a key-value mapping associated with the least key strictly greater than the given key, or null if there is no such key.
K higherKey(K key) Returns the least key strictly greater than the given key, or null if there is no such key.
Map.Entry<K,V> lowerEntry(K key) Returns a key-value mapping associated with the greatest key strictly less than the given key, or null if there is no such key.
K lowerKey(K key) Returns the greatest key strictly less than the given key, or null if there is no such key.
Map.Entry<K,V> pollFirstEntry() Removes and returns a key-value mapping associated with the least key in this map, or null if the map is empty.
Map.Entry<K,V> pollLastEntry() Removes and returns a key-value mapping associated with the greatest key in this map, or null if the map is empty.
Map.Entry<K,V> firstEntry() Returns a key-value mapping associated with the least key in this map, or null if the map is empty.
Map.Entry<K,V> lastEntry() Returns a key-value mapping associated with the greatest key in this map, or null if the map is empty.

TreeMap有兩種排序方式:


天然排序

TreeMap的全部key必須實現Comparable接口,TreeMap會調用keyint compareTo(T o);方法來比較元素的大小,而後將集合元素升序排列.

/** * Compares two keys using the correct comparison method for this TreeMap. */
final int compare(Object k1, Object k2) {
    return comparator==null ? ((Comparable<? super K>)k1).compareTo((K)k2)
        : comparator.compare((K)k1, (K)k2);
}

Java提供的java.lang.Comparable接口僅包含一個int compareTo(T o);方法.大部分經常使用類都實現了該接口(如String, Integer等).

public int compareTo(String anotherString) {
    int len1 = value.length;
    int len2 = anotherString.value.length;
    int lim = Math.min(len1, len2);
    char v1[] = value;
    char v2[] = anotherString.value;

    int k = 0;
    while (k < lim) {
        char c1 = v1[k];
        char c2 = v2[k];
        if (c1 != c2) {
            return c1 - c2;
        }
        k++;
    }
    return len1 - len2;
}

HashMap不一樣,TreeMap判斷兩個key是否相等的惟一標準是:經過compareTo方法比較返回值是否爲0.

public V get(Object key) {
    Entry<K,V> p = getEntry(key);
    return (p==null ? null : p.value);
}

final Entry<K,V> getEntry(Object key) {
    // Offload comparator-based version for sake of performance
    if (comparator != null)
        return getEntryUsingComparator(key);
    if (key == null)
        throw new NullPointerException();
    Comparable<? super K> k = (Comparable<? super K>) key;
    Entry<K,V> p = root;
    while (p != null) {
        int cmp = k.compareTo(p.key);
        if (cmp < 0)
            p = p.left;
        else if (cmp > 0)
            p = p.right;
        else
            return p;
    }
    return null;
}
  • 實現Comparable
/** * @author jifang * @since 16/1/13下午7:50. */
public class Bean implements Comparable<Bean> {

    private boolean isUsed;

    private double rate;

    private String name;

    public Bean(boolean isUsed, double rate, String name) {
        this.isUsed = isUsed;
        this.rate = rate;
        this.name = name;
    }

    @Override
    public int compareTo(Bean anotherBean) {
        double another = (anotherBean.isUsed ? 1 : 0) +
                anotherBean.rate + anotherBean.name.length();
        double self = (isUsed ? 1 : 0) + rate + name.length();
        return (int) (self - another);
    }

    @Override
    public String toString() {
        return "Bean{" +
                "isUsed=" + isUsed +
                ", rate=" + rate +
                ", name='" + name + '\'' +
                '}';
    }
}
@Test
public void clientSortedMap() {
    // value做爲指望的order
    SortedMap<Bean, Integer> map = new TreeMap<>();
    map.put(new Bean(true, 3.14, "true"), 1);
    // 該對象與上面的bean compare會返回0
    map.put(new Bean(false, 3.14, "false"), 1);
    map.put(new Bean(true, 3.14, "false"), 2);
    map.put(new Bean(false, 3.14, "true"), 0);
    System.out.println(map);

    Bean firstKey = map.firstKey();
    System.out.printf("first: %s -> %d%n", firstKey, map.get(firstKey));
    Bean lastKey = map.lastKey();
    System.out.printf("last: %s -> %d%n", lastKey, map.get(lastKey));

    map.remove(firstKey);
    Map.Entry<Bean, Integer> firstEntry = ((TreeMap<Bean, Integer>) map).firstEntry();
    System.out.printf("A first: %s -> %d%n", firstEntry.getKey(), firstEntry.getValue());
}

當執行了remove方法後, TreeMap會對集合中的元素從新索引, 這一點能夠在調試時看到.


自定義排序

TreeMap默認的是使用升序排序,若是須要自定義排序規則,須要爲其傳入一個Comparator實例, 採用定製排序時不要求key實現Comparable.

public class MapClient {

    private Comparator<Bean> comparator = new Comparator<Bean>() {
        @Override
        public int compare(Bean o1, Bean o2) {
            // 返回正數: 說明o1 > o2
            // 返回負數: 說明o1 < o2
            return -o1.compareTo(o2);
        }
    };

    @Test
    public void clientSortedMap() {
        SortedMap<Bean, Integer> map = new TreeMap<>(comparator);
        // ...
    }
}

因爲TreeMap是基於紅黑樹實現,所以相比HashMap性能要慢一點(Hash平均O(1),Tree平均O(lgN)詳細可參考[1. TreeMap源碼解析, 2.紅黑樹的設計與實現(上)]),但其中的key-value已經是有序狀態,無需再有專門的排序操做.所以適用於key有序的場景.


EnumMap

EnumMap是一個須要與枚舉類一塊兒使用的Map,其全部key都必須是單個枚舉類的枚舉值.EnumMap具備如下特徵:

  • EnumMap內部以數組形式存儲,緊湊/高效,是Map全部實現中性能最好的.
  • EnumMap根據key的天然順序(枚舉值在枚舉類的定義順序)來維護key-value順序.
  • EnumMap不容許keynull, 但容許使用null做爲value.
/** * @author jifang * @since 16/1/27 下午4:01. */
public enum ShopListType {

    BLACK_LIST(0, "黑名單"),
    WHITE_LIST(1, "白名單"),
    INVITE_LIST(2, "邀請名單"),
    RECOMMEND_WHITE_LIST(3, "推薦白名單"),
    RECOMMEND_BLACK_LIST(4, "推薦黑名單");

    private int type;

    private String description;

    ShopListType(int type, String description) {
        this.type = type;
        this.description = description;
    }

    public int getValue() {
        return type;
    }

    public String getDescription() {
        return description;
    }
}
@Test
public void clientEnumMap() {
    EnumMap<ShopListType, String> map = new EnumMap<>(ShopListType.class);
    map.put(ShopListType.BLACK_LIST, "黑名單");
    map.put(ShopListType.WHITE_LIST, "白名單");
    System.out.println(map);
}

Set

SetMap關係很是密切, 雖然Map中存放的是key-value, Set中存放的是單個對象, 但從JDK源代碼看, Java是先實現了Map,而後包裝一個空Object來填充全部的value來實現的Set.

private transient HashMap<E,Object> map;

// Dummy value to associate with an Object in the backing Map
private static final Object PRESENT = new Object();
public boolean add(E e) {
    return map.put(e, PRESENT)==null;
}

Set繼承自Collection, 沒有提供額外的方法;


HashSet

HashSetSet接口的典型實現,是Set中用的最多的實現.因爲HashSet是基於HashMap實現的,所以具備以下特色:

  • 不保證元素的排列順序;
  • 不能同步,若是有多個線程同步訪問/修改HashSet, 須要開發人員本身保證同步;
  • 集合元素值能夠爲null;

LinkedHashSet

因爲LinkedHashSet底層是基於LinkedHashMap實現,所以Set能夠記錄元素的插入順序,當遍歷LinkedHashSet時,將會按照元素的添加順序來訪問集合中的元素:

/** * @author jifang * @since 16/1/26 下午2:09. */
public class SetClient {

    @Test
    public void clientLinkedHashSet() {
        Set<Integer> set = new LinkedHashSet<>();
        for (int i = 0; i < 10; ++i) {
            set.add(i);
        }
        for (int i = 19; i >= 10; --i) {
            set.add(i);
        }
        System.out.println(set);
    }
}

LinkedHashSet的優缺點與LinkedHashMap相似.


SortedSet-TreeSet

SortedSet接口繼承自Set,Java爲SortedSet提供了TreeSet實現,因爲SortedSet能夠確保集合元素能夠處於已排序狀態, 所以在Set的基礎上又提供了以下方法:

類型 計算方式
Comparator<? super E> comparator() Returns the comparator used to order the elements in this set, or null if this set uses the natural ordering of its elements.
E first() Returns the first (lowest) element currently in this set.
E last() Returns the last (highest) element currently in this set.
SortedSet<E> tailSet(E fromElement) Returns a view of the portion of this set whose elements are greater than or equal to fromElement.
SortedSet<E> headSet(E toElement) Returns a view of the portion of this set whose elements are strictly less than toElement.
SortedSet<E> subSet(E fromElement, E toElement) Returns a view of the portion of this set whose elements range from fromElement, inclusive, to toElement, exclusive.

TreeSet相比於SortedSet還提供了以下實用方法:

類型 計算方式
E ceiling(E e) Returns the least element in this set greater than or equal to the given element, or null if there is no such element.
E floor(E e) Returns the greatest element in this set less than or equal to the given element, or null if there is no such element.
Iterator<E> descendingIterator() Returns an iterator over the elements in this set in descending order.
NavigableSet<E> descendingSet() Returns a reverse order view of the elements contained in this set.
E higher(E e) Returns the least element in this set strictly greater than the given element, or null if there is no such element.
E lower(E e) Returns the greatest element in this set strictly less than the given element, or null if there is no such element.
E pollFirst() Retrieves and removes the first (lowest) element, or returns null if this set is empty.
E pollLast() Retrieves and removes the last (highest) element, or returns null if this set is empty.

因爲TreeSet底層採用TreeMap實現, 所以其性能特色以及排序規則能夠參考TreeMap.


EnumSet

EnumSet是專門爲枚舉設計的Set,全部的元素必須是單一枚舉類的枚舉值.EnumSet也是有序的,以枚舉值在Enum類內定義的順序來排序;因爲EnumSet沒有暴露任何構造器,所以須要經過他提供的以下static方法來建立EnumSet實例:

  • allOf(Class<E> elementType)
  • complementOf(EnumSet<E> s)
  • copyOf(Collection<E> c)
  • noneOf(Class<E> elementType)
  • of(E first, E... rest)
  • range(E from, E to)
@Test
public void clientEnumSet() {
    EnumSet<ShopListType> set1 = EnumSet.allOf(ShopListType.class);
    System.out.println(set1);

    EnumSet<ShopListType> set2 = EnumSet.noneOf(ShopListType.class);
    System.out.println(set2);
    set2.add(ShopListType.BLACK_LIST);

    System.out.println(set2);
}

EnumSet的內部以位向量的形式存儲,緊湊/高效,所以EnumSet佔用內存小,運行效率高,是Set實現類中性能最好的. 尤爲是批量操做(containsAll(), retainAll())時,若是參數也是EnumSet, 則執行效率很是快(詳細可參考Java EnumSet工做原理初窺).


Collections

Java提供了一個操做List Map Set等集合的工具類Collections, 其提供了大量的工具方法對集合元素進行排序 查找 更新等操做:

  • 排序相關
    sort(List<T> list) sort(List<T> list, Comparator<? super T> c) shuffle(List<?> list) swap(List<?> list, int i, int j) reverse(List<?> list) reverseOrder(Comparator<T> cmp) rotate(List<?> list, int distance)

  • 查找相關
    binarySearch(List<? extends Comparable<? super T>> list, T key) binarySearch(List<? extends T> list, T key, Comparator<? super T> c) indexOfSubList(List<?> source, List<?> target) lastIndexOfSubList(List<?> source, List<?> target)
    max(Collection<? extends T> coll)
    max(Collection<? extends T> coll, Comparator<? super T> comp)
    min(Collection<? extends T> coll)
    min(Collection<? extends T> coll, Comparator<? super T> comp)

  • 更新相關
    addAll(Collection<? super T> c, T... elements)
    fill(List<? super T> list, T obj)
    nCopies(int n, T o)

  • 不可變集合視圖
    unmodifiableCollection(Collection<? extends T> c)
    unmodifiableList(List<? extends T> list)
    unmodifiableMap(Map<? extends K,? extends V> m)
    unmodifiableSet(Set<? extends T> s)
    unmodifiableSortedMap(SortedMap<K,? extends V> m)
    unmodifiableSortedSet(SortedSet<T> s)

  • 單元素集合
    Set<T> singleton(T o)
    singletonList(T o)
    singletonMap(K key, V value)

  • 空集合
    emptyList()
    emptyMap()
    emptySet()
    Collections提供了三個靜態變量來表明一個空集合
    static List EMPTY_LIST
    static Map EMPTY_MAP
    static Set EMPTY_SET

  • 同步集合
    詳見Java 併發基礎


遺留的集合

Java還提供了一些集合工具:Hashtable Vactor Stack Enumeration StringTokenizer(Enumeration的一個實現類,其功能相似於Stringsplit(),但不支持正則,實現將字符串進行分割, 而後迭代取出), 這些集合工具都是從Java 1.0開始就存在的, 但其實現要麼性能較低(須要保持線程同步), 要麼方法名繁瑣(如hasMoreElements()), 如今已經不多使用,並且其使用方法也與前面的集合相似, 所以在此就不作過多介紹了. 若是在實際開發中會遇到還在使用這些工具的代碼(好比Dom4j),能夠參考JDK文檔.


Properties

PropertiesHashtable的子類,他能夠把Map和屬性文件關聯起來,從而能夠把Map對象中的key-value寫入屬性文件, 也能夠將屬性文件中的」屬性名=屬性值」加載到Map中,因爲屬性文件中的屬性名/屬性值都是String,所以Propertieskey-value都只能是String.Properties提供了以下方法來讀寫內存中的key-value.

方法 描述
String getProperty(String key) Searches for the property with the specified key in this property list.
String getProperty(String key, String defaultValue) Searches for the property with the specified key in this property list.
Object setProperty(String key, String value) Calls the Hashtable method put.
Enumeration<?> propertyNames() Returns an enumeration of all the keys in this property list, including distinct keys in the default property list if a key of the same name has not already been found from the main properties list.
Set<String> stringPropertyNames() Returns a set of keys in this property list where the key and its corresponding value are strings, including distinct keys in the default property list if a key of the same name has not already been found from the main properties list.

Properties還提供了讀寫屬性文件的方法:

方法 描述
void list(PrintStream/PrintWriter out) Prints this property list out to the specified output stream/writer.
void load(InputStream/Reader in) Reads a property list (key and element pairs) from the input byte/character stream.
void store(OutputStream/Write out, String comments) Writes this property list (key and element pairs) in this Properties table to the output stream/write in a format suitable for loading into a Properties table using the load(InputStream/Reader) method.
  • common.properties
dubbo.version=1.0.0

## Data Source
mysql.driver.class=com.mysql.jdbc.Driver
mysql.url=jdbc:mysql://192.168.9.166:3306/common
mysql.user=admin
mysql.password=admin
  • client
@Test
public void clientProperties() throws IOException {
    Properties properties = new Properties();
    properties.load(ClassLoader.getSystemResourceAsStream("common.properties"));
    System.out.println(properties.get("mysql.driver.class"));
    properties.put("mysql.user", "root");
    properties.put("mysql.password", "root");
    properties.store(new FileOutputStream("common.properties"), "comment");
}

Properties還能夠從XML中加載key-value,也能夠以XML形式保存,其用法與普通.properties文件相似.


參考:
給jdk寫註釋系列之jdk1.6容器
grepcode.com
數據結構與STL系列博客
oracle.javase.docs.api
Java編程思想
瘋狂Java講義
Google Guava官方教程
數據結構與算法分析
相關文章
相關標籤/搜索