美文网首页
JVAV集合框架

JVAV集合框架

作者: 独自闯天涯的码农 | 来源:发表于2022-03-28 22:39 被阅读0次

一、Java集合框架分类

1、Set:无序,不可重复;
2、List:有序,可重复;
3、Queue:队列集合;
4、Map:映射关系;

1、Java集合和数组区别

  1. 数组长度在初始化时指定;集合可以保存不确定的数据;
  2. 数组元素可以时基本类型和对象;集合只能保存对象(实际是引用);

2、Java集合继承关系

Java集合类主要由两个接口派生:Collection和Map

Collection Map

3、Collection接口

1. 接口定义:
public interface Collection<E> extends Iterable<E> {
    
    int size();

    boolean isEmpty();

    boolean contains(Object o);

    Iterator<E> iterator();

    Object[] toArray();

    <T> T[] toArray(T[] a);

    boolean add(E e);

    boolean remove(Object o);

    boolean containsAll(Collection<?> c);

    boolean addAll(Collection<? extends E> c);

    boolean removeAll(Collection<?> c);

    default boolean removeIf(Predicate<? super E> filter) {
        Objects.requireNonNull(filter);
        boolean removed = false;
        final Iterator<E> each = iterator();
        while (each.hasNext()) {
            if (filter.test(each.next())) {
                each.remove();
                removed = true;
            }
        }
        return removed;
    }

    boolean retainAll(Collection<?> c);

    void clear();

    boolean equals(Object o);

    int hashCode();

    @Override
    default Spliterator<E> spliterator() {
        return Spliterators.spliterator(this, 0);
    }

    default Stream<E> stream() {
        return StreamSupport.stream(spliterator(), false);
    }

    default Stream<E> parallelStream() {
        return StreamSupport.stream(spliterator(), true);
    }
}

Iterable接口可迭代的,它是Collection的父接口,它的方法iterator()返回Iterator被称为迭代器,主要用于遍历集合中的元素;

2. Set

Set即Collection,唯一区别Set不能有重复元素;

3. List

List增加根据索引获取元素的方法;

4. Queue

队列先进先出的容器;

public interface Queue<E> extends Collection<E> {
    boolean add(E var1);
    E element();
    boolean offer(E var1);
    E remove();
    E poll();
    E peek();
}

4、Map接口

Map保存具有映射关系的数据;key不同,value可相同;
Map的key组成了Set集合;
Map的value组成了List集合;

注意:

List遍历删除元素需要使用iterator,用for循环调用list.remove方法导致索引也在变化,删除会报错

Iterator<String> it = list.iterator();
while(it.hasNext()){
    String x = it.next();
    if(x.equals("del")){
        it.remove();
    }
}

二、ArrayList

  • 数组实现;
  • 有容量限制,默认为10,超出限制会增加50%容量,通过System.arraycopy()复制到新的数组;自动扩容;
  • 优势:末尾增加和查找性能高;
  • 劣势:插入和删除性能低;

三、LinkedList

  • 双向链表实现;
  • 无容量限制;
  • 优势:插入删除性能高;
  • 劣势:查找性能低;

四、HashMap

  • Hash表、链表、红黑树实现;
  • 允许null键/值
  • 非同步
  • 不保证有序

三个重要的参数:
1、容量Capacity,默认16
Capacity就是bucket(桶)的大小;
2、负载因子Load factor,默认0.75
Load factor就是bucket(桶)填满程度的最大比例;
如果bucket中entries数目大于Capacity * Load factor,将会调整bucket大小为当前两倍;
3、TREEIFY_THRESHOLD,默认为8
使用树而不是列表的 bin 计数阈值;大于 2 并且应至少为 8 以符合假设;

static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16
static final int MAXIMUM_CAPACITY = 1 << 30;
static final float DEFAULT_LOAD_FACTOR = 0.75f;
static final int TREEIFY_THRESHOLD = 8;

1、put函数的实现

  1. 对key的hashCode()做hash,然后再计算index;
  2. 如果没碰撞直接放到bucket里;
  3. 如果碰撞了,以链表的形式存在buckets后;
  4. 如果碰撞导致链表过长(大于等于 TREEIFY_THRESHOLD),就把链表转换成红黑树;
  5. 如果节点已经存在就替换old value (保证key的唯一性)
  6. 如果bucket满了(超过 load factor*current capacity ),就要resize。

具体代码的实现如下:

public V put(K key, V value) {
        //对key的hashcode做hash值
        return putVal(hash(key), key, value, false, true);
}


final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
                   boolean evict) {
        Node<K,V>[] tab; Node<K,V> p; int n, i;
        //tab为空则创建
        if ((tab = table) == null || (n = tab.length) == 0)
            n = (tab = resize()).length;
        //计算index,并对null处理
        if ((p = tab[i = (n - 1) & hash]) == null)
            tab[i] = newNode(hash, key, value, null);
        else {
            Node<K,V> e; K k;
            //节点存在
            if (p.hash == hash &&
                ((k = p.key) == key || (key != null && key.equals(k))))
                e = p;
            //该链为树
            else if (p instanceof TreeNode)
                e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
            //该链为链表
            else {
                for (int binCount = 0; ; ++binCount) {
                    if ((e = p.next) == null) {
                        p.next = newNode(hash, key, value, null);
                        if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
                            treeifyBin(tab, hash);
                        break;
                    }
                    if (e.hash == hash &&
                        ((k = e.key) == key || (key != null && key.equals(k))))
                        break;
                    p = e;
                }
            }
            //写入
            if (e != null) { // existing mapping for key
                V oldValue = e.value;
                if (!onlyIfAbsent || oldValue == null)
                    e.value = value;
                afterNodeAccess(e);
                return oldValue;
            }
        }
        ++modCount;
        //超过则扩容
        if (++size > threshold)
            resize();
        afterNodeInsertion(evict);
        return null;
    }

2、get函数的实现

  1. bucket里的第一个节点,直接命中;
  2. 如果有冲突,则通过key.equals(k)去查找对应的entry
    若为树,则在树中通过key.equals(k)查找,O(logn);
    若为链表,则在链表中通过key.equals(k)查找,O(n)。

具体代码的实现如下:

public V get(Object key) {
        Node<K,V> e;
        return (e = getNode(hash(key), key)) == null ? null : e.value;
    }


final Node<K,V> getNode(int hash, Object key) {
        Node<K,V>[] tab; Node<K,V> first, e; int n; K k;
        if ((tab = table) != null && (n = tab.length) > 0 &&
            (first = tab[(n - 1) & hash]) != null) {
            //直接命中
            if (first.hash == hash && // always check first node
                ((k = first.key) == key || (key != null && key.equals(k))))
                return first;
            //未命中
            if ((e = first.next) != null) {
                 //在树中
                if (first instanceof TreeNode)
                    return ((TreeNode<K,V>)first).getTreeNode(hash, key);
                do {
                    if (e.hash == hash &&
                        ((k = e.key) == key || (key != null && key.equals(k))))
                        return e;
                } while ((e = e.next) != null);
            }
        }
        return null;
    }

3、hash函数的实现

将key的hashcode高16位和低16位异或

static final int hash(Object key) {
        int h;
        return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
    }

在计算下标时,table长度为n的2次幂,将获取的hash值与n-1进行与运算
tab[(n - 1) & hash]
如果没有高16位和低16位异或运算,很容易冲突;

4、resize函数的实现

因为是2次幂的扩展,所以元素的位置要么是原位置,要么是在原位置再移动2次幂的位置;因此,我们在扩充HashMap的时候,不需要重新计算hash,只需要看看原来的hash值新增的那个bit是1还是0就好了,是O的话索引没变,是1的话索引变成“原索引+oldCap”。

final Node<K,V>[] resize() {
        Node<K,V>[] oldTab = table;
        int oldCap = (oldTab == null) ? 0 : oldTab.length;
        int oldThr = threshold;
        int newCap, newThr = 0;
        if (oldCap > 0) {
            if (oldCap >= MAXIMUM_CAPACITY) {
                threshold = Integer.MAX_VALUE;
                return oldTab;
            }
            else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&
                     oldCap >= DEFAULT_INITIAL_CAPACITY)
                newThr = oldThr << 1; // double threshold
        }
        else if (oldThr > 0) // initial capacity was placed in threshold
            newCap = oldThr;
        else {               // zero initial threshold signifies using defaults
            newCap = DEFAULT_INITIAL_CAPACITY;
            newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);
        }
        if (newThr == 0) {
            float ft = (float)newCap * loadFactor;
            newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ?
                      (int)ft : Integer.MAX_VALUE);
        }
        threshold = newThr;
        @SuppressWarnings({"rawtypes","unchecked"})
            Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap];
        table = newTab;
        if (oldTab != null) {
            for (int j = 0; j < oldCap; ++j) {
                Node<K,V> e;
                if ((e = oldTab[j]) != null) {
                    oldTab[j] = null;
                    if (e.next == null)
                        newTab[e.hash & (newCap - 1)] = e;
                    else if (e instanceof TreeNode)
                        ((TreeNode<K,V>)e).split(this, newTab, j, oldCap);
                    else { // preserve order
                        Node<K,V> loHead = null, loTail = null;
                        Node<K,V> hiHead = null, hiTail = null;
                        Node<K,V> next;
                        do {
                            next = e.next;
                            if ((e.hash & oldCap) == 0) {
                                if (loTail == null)
                                    loHead = e;
                                else
                                    loTail.next = e;
                                loTail = e;
                            }
                            else {
                                if (hiTail == null)
                                    hiHead = e;
                                else
                                    hiTail.next = e;
                                hiTail = e;
                            }
                        } while ((e = next) != null);
                        if (loTail != null) {
                            loTail.next = null;
                            newTab[j] = loHead;
                        }
                        if (hiTail != null) {
                            hiTail.next = null;
                            newTab[j + oldCap] = hiHead;
                        }
                    }
                }
            }
        }
        return newTab;
    }

五、TreeMap

  • 红黑树实现
  • 保证数据有序(按Key排序)
  • 中序遍历LDR

1、put函数的实现

public V put(K key, V value) {
        TreeMapEntry<K,V> t = root;
        if (t == null) {
            compare(key, key); // type (and possibly null) check

            root = new TreeMapEntry<>(key, value, null);
            size = 1;
            modCount++;
            return null;
        }
        int cmp;
        TreeMapEntry<K,V> parent;
        // split comparator and comparable paths
        Comparator<? super K> cpr = comparator;
        if (cpr != null) {
            do {
                parent = t;
                cmp = cpr.compare(key, t.key);
                if (cmp < 0)
                    t = t.left;
                else if (cmp > 0)
                    t = t.right;
                else
                    return t.setValue(value);
            } while (t != null);
        }
        else {
            if (key == null)
                throw new NullPointerException();
            @SuppressWarnings("unchecked")
                Comparable<? super K> k = (Comparable<? super K>) key;
            do {
                parent = t;
                cmp = k.compareTo(t.key);
                if (cmp < 0)
                    t = t.left;
                else if (cmp > 0)
                    t = t.right;
                else
                    return t.setValue(value);
            } while (t != null);
        }
        TreeMapEntry<K,V> e = new TreeMapEntry<>(key, value, parent);
        if (cmp < 0)
            parent.left = e;
        else
            parent.right = e;
        fixAfterInsertion(e);
        size++;
        modCount++;
        return null;
    }

2、get函数的实现

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

final TreeMapEntry<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();
        @SuppressWarnings("unchecked")
            Comparable<? super K> k = (Comparable<? super K>) key;
        TreeMapEntry<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;
    }

3、successor函数的实现

查找后继

static <K,V> TreeMapEntry<K,V> successor(TreeMapEntry<K,V> t) {
        if (t == null)
            return null;
        else if (t.right != null) {
            TreeMapEntry<K,V> p = t.right;
            while (p.left != null)
                p = p.left;
            return p;
        } else {
            TreeMapEntry<K,V> p = t.parent;
            TreeMapEntry<K,V> ch = t;
            while (p != null && ch == p.right) {
                ch = p;
                p = p.parent;
            }
            return p;
        }
    }

3、predecessor函数的实现

查找前继

static <K,V> TreeMapEntry<K,V> predecessor(TreeMapEntry<K,V> t) {
        if (t == null)
            return null;
        else if (t.left != null) {
            TreeMapEntry<K,V> p = t.left;
            while (p.right != null)
                p = p.right;
            return p;
        } else {
            TreeMapEntry<K,V> p = t.parent;
            TreeMapEntry<K,V> ch = t;
            while (p != null && ch == p.left) {
                ch = p;
                p = p.parent;
            }
            return p;
        }
    }

六、LinkedHashMap

  • Hash表和双向链表实现
  • 双向链表保证迭代顺序是默认是插入顺序

1、重要参数

此链接哈希映射的迭代排序方法:
true :用于访问顺序,
false: 用于插入顺序
final boolean accessOrder;

2、三个重要方法

afterNodeAccess:节点访问后;put之后,更新列表,把最近访问的放到最后;
afterNodeInsertion:节点插入后;如果溢出了,移除第一个;
afterNodeRemoval:节点移除后;将节点从双向链表中删除;

这三个函数保证双向链表次序;保证节点顺序从年老到年轻;

    void afterNodeAccess(Node<K,V> e) { // move node to last
        LinkedHashMapEntry<K,V> last;
        if (accessOrder && (last = tail) != e) {
            LinkedHashMapEntry<K,V> p =
                (LinkedHashMapEntry<K,V>)e, b = p.before, a = p.after;
            p.after = null;
            if (b == null)
                head = a;
            else
                b.after = a;
            if (a != null)
                a.before = b;
            else
                last = b;
            if (last == null)
                head = p;
            else {
                p.before = last;
                last.after = p;
            }
            tail = p;
            ++modCount;
        }
    }


    void afterNodeInsertion(boolean evict) { // possibly remove eldest
        LinkedHashMapEntry<K,V> first;
        if (evict && (first = head) != null && removeEldestEntry(first)) {
            K key = first.key;
            removeNode(hash(key), key, null, false, true);
        }
    }


    void afterNodeRemoval(Node<K,V> e) { // unlink
        LinkedHashMapEntry<K,V> p =
            (LinkedHashMapEntry<K,V>)e, b = p.before, a = p.after;
        p.before = p.after = null;
        if (b == null)
            head = a;
        else
            b.after = a;
        if (a == null)
            tail = b;
        else
            a.before = b;
    }

2、get方法

如果是accessOrder = true :用于访问顺序,更新列表顺序;

public V get(Object key) {
        Node<K,V> e;
        if ((e = getNode(hash(key), key)) == null)
            return null;
        if (accessOrder)
            afterNodeAccess(e);
        return e.value;
    }

相关文章

网友评论

      本文标题:JVAV集合框架

      本文链接:https://www.haomeiwen.com/subject/atpmjrtx.html