Android 集合总结

320 阅读3分钟

非 Concurrent 集合

1. Set

1.1 HashSet

内部默认使用 HashMap ,还提供了个私有的构造函数给 LinkedHashSet 使用, 其map 改为了 LinkedHashMap

 public boolean add(E e) {
        return map.put(e, PRESENT)==null;
 }

所以 HashSet 的本质只是将存入的参数作为 key 传入到 map 当中,由于 HashMap 的 key 唯一,进而保证了 HashSet 存储唯一


1.2 TreeSet

内部也维护了一个 map ,这个 map 为 TreeMap

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

public boolean add(E e) {
    return m.put(e, PRESENT)==null;
}

TreeSet 的操作都是通过 TreeMap 完成

TreeMapEntry(K key, V value, TreeMapEntry<K,V> parent) {
    this.key = key;
    this.value = value;
    this.parent = parent;
}

可以很明显的看出,TreeMap 内部其实是一个 二叉树,通过源码看到其 put 实际上就是 二叉树的插入 ,而 fixAfterInsertion 标表明 TreeSet 内部其实是使用的 红黑树 进行存储


1.3 LinkedHashSet

内部没有实现 add 等方法,但是我们通过他的构造函数可以看到

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

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

可以看到 LinkedHashSet 是继承的 HashSet ,它仅仅是将 HashSet 里的 map 换成了 LinkedHashMap ,所以 LinkedHashSet 本质与 HashSet 类似,都是将存入的参数当为 key 传入 map 中, 只是 LinkedHashSetmap 使用的是 LinkedHashMap


2. List

2.1 ArrayList

ArrayList 会在创建时,创建一个 数组 elementData ,内部的储存与取出都是通过数组来维护的,数组默认长度为10

public boolean add(E e) {
    ensureCapacityInternal(size + 1);  // Increments modCount!!
    elementData[size++] = e;
    return true;
}

private void grow(int minCapacity) { 
    int oldCapacity = elementData.length;
    int newCapacity = oldCapacity + (oldCapacity >> 1);
    if (newCapacity - minCapacity < 0)  newCapacity = minCapacity;
    if (newCapacity - MAX_ARRAY_SIZE > 0)  newCapacity = hugeCapacity(minCapacity);
    elementData = Arrays.copyOf(elementData, newCapacity);
}

简而言之,ArrayList 的扩容算法就是 当超过了原始长度,扩容为 原始长度的 1.5倍,然后通过 Arrays.copyOf 进行扩容,最后将加入的值放入数组的指定位置,取值就是直接通过 elementData[index] 获取


2.2 Vector

Vector 也会在创建的时候,创建一个 数组 elementData ,仔细查看源码后,几乎与 ArrayList 相同

public synchronized boolean add(E e) {
    modCount++;
    ensureCapacityHelper(elementCount + 1);
    elementData[elementCount++] = e;
    return true;
}

private void grow(int minCapacity) {
    // overflow-conscious code
    int oldCapacity = elementData.length;
    int newCapacity = oldCapacity + ((capacityIncrement > 0) ? capacityIncrement : oldCapacity);
    if (newCapacity - minCapacity < 0) newCapacity = minCapacity;
    if (newCapacity - MAX_ARRAY_SIZE > 0) newCapacity = hugeCapacity(minCapacity);
    elementData = Arrays.copyOf(elementData, newCapacity);
    }

可以察觉,Vector 仅仅是在ArrayList 源码基础上,对 add get set 等数据操作上的方法加了 synchronized ,由于是加在方法上,意味着当一个线程在调用 add 的时候,如果另一个线程需要调用 get 就会阻塞,等待锁的释放。开销会增加。


2.3 Stack

Stack 栈,先进后出,继承自 Vector

public E push(E item) {
    addElement(item);
    return item;
}

入栈就是普通List里的add,将数据加入到数组后面

public synchronized E peek() {
    int len = size();
    if (len == 0)
        throw new EmptyStackException();
    return elementAt(len - 1);
}

peek 查看栈顶元素,实际上就是看数组的最后一个参数

public synchronized E pop() {
    E  obj;
    int len = size();
    obj = peek();
    removeElementAt(len - 1);
    return obj;
    }

pop 查看最后数组最后一个数后,并且移除最后一个数,也就是栈的先进先出。


2.4 LinkedList

LinkedList 即实现了 List 接口,又实现了 Deque 接口,而其内部不再是数组,而是链表形式。

private static class Node<E> {
    E item;
    Node<E> next;
    Node<E> prev;
    Node(Node<E> prev, E element, Node<E> next) {
        this.item = element;
        this.next = next;
        this.prev = prev;
    }
}

所以其增加就是链表添加尾部节点的代码

public boolean add(E e) {
    linkLast(e);
    return true;
}

void linkLast(E e) {
    final Node<E> l = last;
    final Node<E> newNode = new Node<>(l, e, null);
    last = newNode;
    if (l == null)
        first = newNode;
    else
        l.next = newNode;
    size++;
    modCount++;
}

同样由于是链表操作,index查找方式,是用的二分法进行查找,比较耗时

Node<E> node(int index) {
    // assert isElementIndex(index);

    if (index < (size >> 1)) {
        Node<E> x = first;
        for (int i = 0; i < index; i++)
            x = x.next;
        return x;
    } else {
        Node<E> x = last;
        for (int i = size - 1; i > index; i--)
            x = x.prev;
        return x;
    }
}

链表的的获取,相对快一些,头与尾的获取是通过维护 firstlast 节点

    public E getFirst() {
        final Node<E> f = first;
        if (f == null)
            throw new NoSuchElementException();
        return f.item;
    }

3. Map

3.1 HashMap

HashMap 不主要讲了,讲的话篇幅较长,大家可脱离此博客自行总结。总而言之HashMap 内部维护的是 数组+链表结构,链表会在长度超过8的时候转为红黑数,内部通过计算key的hashcode,再通过