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官方教程
数据结构与算法分析
相关文章
相关标签/搜索