zoukankan      html  css  js  c++  java
  • java容器的数据结构-ArrayList,LinkList,HashMap

    ArrayList:
    初始容量为10,底层实现是一个数组,Object[] elementData
    自动扩容机制,当添加一个元素时,数组长度超过了elementData.leng,则会按照1.5倍进行扩容
    private void grow() {
            int oldCapacity = elementData.length;
            int newCapacity = oldCapacity + (oldCapacity >> 1);
            elementData = Arrays.copyOf(elementData, newCapacity);
        }
    contains()方法的实现 ,注意ArrayList中是可以放null对象的
    public boolean contains(Object o) {
            return indexOf(o) >= 0;
        }
     
    public int indexOf(Object o) {
            if (o == null) {              //首先判断对象是否为null,不是的话才可以执行o.equals(elementData[i])
                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;
        }
    ArrayList如何转化成数组呢?
     * <p>This method acts as bridge between array-based and collection-based
         * APIs.
         *
         * @return an array containing all of the elements in this list in
         *         proper sequence
         */
        public Object[] toArray() {
            return Arrays.copyOf(elementData, size);
        }
    随机获取某个位置的元素
     /**
         * Returns the element at the specified position in this list.
         */
        public E get(int index) {
            rangeCheck(index);
     
            return elementData(index);
        }
     
     @SuppressWarnings("unchecked")
        E elementData(int index) {
            return (E) elementData[index];
        }
    添加一个元素,首先判断是否需要扩容
    public boolean add(E e) {
            ensureCapacityInternal(size + 1);  // Increments modCount!!
            elementData[size++] = e;
            return true;
        }
    retainAll()方法,取两个集合的交集
    /**
         * Retains only the elements in this list that are contained in the
         * specified collection.  In other words, removes from this list all
         * of its elements that are not contained in the specified collection.
         */
        public boolean retainAll(Collection<?> c) {
            Objects.requireNonNull(c);
            return batchRemove(c, true);
        }
    遍历整个容器
        /**
         * Returns an iterator over the elements in this list in proper sequence.
         * @return an iterator over the elements in this list in proper sequence
         */
        public Iterator<E> iterator() {
            return new Itr();
        }
     
     private class Itr implements Iterator<E> {
            int cursor;       // index of next element to return
     
            public boolean hasNext() {
                return cursor != size;
            }
     
            @SuppressWarnings("unchecked")
            public E next() {
                int i = cursor;
                Object[] elementData = ArrayList.this.elementData;
                if (i >= elementData.length)
                    throw new ConcurrentModificationException();
                cursor = i + 1;
                return (E) elementData[lastRet = i];
            }
        }
     
     
    LinkedList:基于链表实现,插入和删除非常高效,只需要改变指针指向的位置,不需要像ArrayList那样整体挪动元素。LinkedList中主要提供了三个基本属性,size,first,last,初始容量size为0,可以简单的把first,last理解为两个指针,分别指向第一个节点和最后一个节点。
    /**
         * Pointer to first node.
         * Invariant: (first == null && last == null) ||
         *            (first.prev == null && first.item != null)  
         */
        transient Node<E> first;    //first本身也是一个Node,没有previous元素,仅仅是指向队列首元素的标志
     
        /**
         * Pointer to last node.
         * Invariant: (first == null && last == null) ||
         *            (last.next == null && last.item != null)
         */
        transient Node<E> last;     //last本身也是一个Node,没有next元素,仅仅是指向队列末尾元素的标志
     
    链表中每一个元素都是一个Node(节点)
     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;
            }
        }
     
    在链表的某个位置前插入一个新的节点,要让这三个节点的指针互相指向。
    /**
         * Inserts element e before non-null Node succ.
         */
        void linkBefore(E e, Node<E> succ) {
            // assert succ != null;
            final Node<E> pred = succ.prev;
            final Node<E> newNode = new Node<>(pred, e, succ);
            succ.prev = newNode;
            if (pred == null)
                first = newNode;
            else
                pred.next = newNode;
            size++;
            modCount++;
        }
     
    让元素成为链表中的第一个元素,该方法只用于研究使用
        /**
         * Links e as first element.
         */
        private void linkFirst(E e) {
            final Node<E> f = first;
            final Node<E> newNode = new Node<>(null, e, f);  //创建了一个节点,prev为null,last为原先的第一个元素
            first = newNode;         //让first指针指向第一个元素 newNode
            if (f == null)
                last = newNode;      //如果容器里开始一个元素都没有,则让last也指向新添加的元素newNode
            else                       如果容器里开始就已经有元素了,则first此时已经变为第二个元素
                f.prev = newNode;
            size++;
            modCount++;
        }
    获得容器的第一个元素
        /**
         * Returns the first element in this list.
         */
        public E getFirst() {
            final Node<E> f = first;
            if (f == null)       //如果容器里一个元素都没有,则抛出异常,如果有元素,则第一个元素就是first
                throw new NoSuchElementException();
            return f.item;
        }
     
    获得容器中的最后一个元素
     /**
         * Returns the last element in this list.
         */
        public E getLast() {
            final Node<E> l = last;
            if (l == null)    ////如果容器里一个元素都没有,则抛出异常,如果有元素,则最后一个元素就是last
                throw new NoSuchElementException();
            return l.item;
        }
     
    删掉容器中的第一个元素
    /**
         * Unlinks non-null first node f.
         */
        private E unlinkFirst(Node<E> f) {
            final E element = f.item;
            final Node<E> next = f.next;  //获得下一个节点,当删掉第一个节点后,后一个节点就变成第一个节点了
            f.item = null;      //将节点中的item赋值为空
            f.next = null;      // 将指针next赋值为空
            first = next;
            if (next == null)
                last = null;
            else
                next.prev = null;  
            size--;
            modCount++;
            return element;
        }
     
    删掉容器中间的某个元素
     /**
         * Removes the first occurrence of the specified element from this list,
         * if it is present.  
         */
     public boolean remove(Object o) {
            if (o == null) {         //首先判断删除的Element是否为空
                for (Node<E> x = first; x != null; x = x.next) {
                    if (x.item == null) {
                        unlink(x);
                        return true;
                    }
                }
            } else {
                for (Node<E> x = first; x != null; x = x.next) {
                    if (o.equals(x.item)) {
                        unlink(x);
                        return true;
                    }
                }
            }
            return false;
        }
     
     /**
         * Unlinks non-null node x.
         */
        E unlink(Node<E> x) {       该方法至关重要,删除中间节点的操作 
            final E element = x.item;
            final Node<E> next = x.next;       //获得当前结点的下一个节点
            final Node<E> prev = x.prev;       //获得当前结点的上一个节点
     
            if (prev == null) {
                first = next;                  //如果prev是空值的话,则说明当前删的是第一个节点,那么删除后first
            } else {                             指针将指向x的下一个节点next
                prev.next = next;              //如果不是的话,则有左边的指针指向关系
                x.prev = null;       
            }
     
            if (next == null) {
                last = prev;
            } else {
                next.prev = prev;
                x.next = null;
            }
     
            x.item = null;                     //总之,要删掉节点x,需要将prev,next,item都赋值为null
            size--;
            modCount++;
            return element;
        }
     
    判断是否包含某个元素,元素在哪个具体位置(index)
    public boolean contains(Object o) {
            return indexOf(o) != -1;
        }
     /**
         * Returns the index of the first occurrence of the specified element
         * in this list, or -1 if this list does not contain the element.
         */
        public int indexOf(Object o) {
            int index = 0;           //设置一个计数器
            if (o == null) {
                for (Node<E> x = first; x != null; x = x.next) {
                    if (x.item == null)
                        return index;
                    index++;         //每次循环,如果不是得话就++
                }
            } else {
                for (Node<E> x = first; x != null; x = x.next) {
                    if (o.equals(x.item))
                        return index;
                    index++;
                }
            }
            return -1;
        }
     
    遍历整个容器,返回ListIterator双向迭代器
     /**
         * The list-iterator is <i>fail-fast</i>: if the list is structurally
         * modified at any time after the Iterator is created, in any way except
         * through the list-iterator's own {@code remove} or {@code add}
         * methods, the list-iterator will throw a
         * {@code ConcurrentModificationException}.  Thus, in the face of
         * concurrent modification, the iterator fails quickly and cleanly, rather
         * than risking arbitrary, non-deterministic behavior at an undetermined
         * time in the future.
         */
        public ListIterator<E> listIterator(int index) {
            checkPositionIndex(index);
            return new ListItr(index);
        }
     
    fail-fast 机制是java集合(Collection)中的一种错误机制。当多个线程对同一个集合的内容进行操作时,就可能会产生fail-fast事件。例如:当某一个线程A通过iterator去遍历某集合的过程中,若该集合的内容被其他线程所改变了;那么线程A访问集合时,就会抛出ConcurrentModificationException异常,产生fail-fast事件(快速失败行为)。
    (注意:结构上的修改是指添加或删除一个或多个映射关系的任何操作;仅改变与实例已经包含的键关联的值不是结构上的修改。)
     
     

    迭代器指向的位置是元素之前的位置,如下图所示:

        

        

    这里假设集合List由四个元素List1、List2、List3和List4组成,当使用语句Iterator it = List.Iterator()时,迭代器it指向的位置是上图中Iterator1指向的位置,当执行语句it.next()之后,迭代器指向的位置后移到上图Iterator2所指向的位置。

    Iterator迭代器包含的方法有:

    hasNext():如果迭代器指向位置后面还有元素,则返回 true,否则返回false

    next():返回集合中Iterator指向位置后面的元素

    ListIterator迭代器包含的方法有:

    hasNext():以正向遍历列表时,如果列表迭代器后面还有元素,则返回 true,否则返回false

    hasPrevious():如果以逆向遍历列表,列表迭代器前面还有元素,则返回 true,否则返回false

    next():返回列表中ListIterator指向位置后面的元素

    previous():返回列表中ListIterator指向位置前面的元素

     
     
    HashMap:默认初始化大小为16
     /**
         * The default initial capacity - MUST be a power of two.
         */
        static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16
     
    负载因子为0.75
     /**
         * The load factor used when none specified in constructor.
         */
        static final float DEFAULT_LOAD_FACTOR = 0.75f;
     
    当超过length*factor时,HashMap就会自动扩容,按照两倍进行扩容。
     
    HashMap底层由数组实现(具体来讲是数据加链表)  Node<K,V>[] table;
     
    static class Node<K,V> implements Map.Entry<K,V> {
            final int hash;
            final K key;
            V value;
            Node<K,V> next;
     
            Node(int hash, K key, V value, Node<K,V> next) {
                this.hash = hash;
                this.key = key;
                this.value = value;
                this.next = next;
            }
        }
     
    往HashMap中放入一个Mapping
    public V put(K key, V value) {
            if (key == null)
                return putForNullKey(value);
            int hash = hash(key);                        //首先获得key的hash值
            int i = indexFor(hash, table.length);        //其次计算出应该放在table的哪个位置(桶)
            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;                //如果要插入的Mapping的Hash值和key与桶中的某个
                    e.recordAccess(this);             Mapping完全相同,则替换掉它的value值
                    return oldValue;
                }
            }
    
            modCount++;
            addEntry(hash, key, value, i);
            return null;
        }
     
    final int hash(Object k) {
            int h = 0;
            h ^= k.hashCode();
    
            h ^= (h >>> 20) ^ (h >>> 12);
            return h ^ (h >>> 7) ^ (h >>> 4);
        }
    static int indexFor(int h, int length) {
            return h & (length-1);
        }

    当需要插入的keynull时,调用putForNullKey方法处理:

     
     private V putForNullKey(V value) {
            for (Entry<K,V> e = table[0]; e != null; e = e.next) {
                if (e.key == null) {
                    V oldValue = e.value;
                    e.value = value;
                    e.recordAccess(this);
                    return oldValue;
                }
            }
            modCount++;
            addEntry(0, null, value, 0);
            return null;
        }
     
       当需要插入的keynull时,调用putForNullKey方法处理:
     
     private V putForNullKey(V value) {
            for (Entry<K,V> e = table[0]; e != null; e = e.next) {
                if (e.key == null) {
                    V oldValue = e.value;    //如果HashMap中有<null,value>这样的Node,则一定是放在table的
                    e.value = value;           首位置,因为null的hash值为0
                    e.recordAccess(this);
                    return oldValue;
                }
            }
            modCount++;
            addEntry(0, null, value, 0);
            return null; 
        }
    添加一个Entry,首先判断是否需要扩容,size表示table(数组)当前的元素个数,扩容时按照两倍的方式扩容,之后还需要重哈希(rehash)
     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);
        }
    真正意义上的添加Entry,首先获得链表原来的头节点e,然后构造一个新的节点,使其next指针指向e:
    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++;
        }
     
     
    在HashMap中如何根据key获得相应的value值呢?
    public V get(Object key) {
            if (key == null)           //首先判断key是否为null
                return getForNullKey();
            Entry<K,V> entry = getEntry(key);
    
            return null == entry ? null : entry.getValue();
        }
     
    private V getForNullKey() {
            for (Entry<K,V> e = table[0]; e != null; e = e.next) {
                if (e.key == null)
                    return e.value;
            }
            return null;
        }
     
    final Entry<K,V> getEntry(Object key) {
            int hash = (key == null) ? 0 : hash(key);  //首先获得key的hash值
            for (Entry<K,V> e = table[indexFor(hash, table.length)];//获得key在table中的index
                 e != null;
                 e = e.next) {
                Object k;
                if (e.hash == hash &&         //如果完全相同,则返回key对应的Entry
                    ((k = e.key) == key || (key != null && key.equals(k))))
                    return e;
            }
            return null;
        }

     

  • 相关阅读:
    黑鲨2无限重启 把竞技按钮调到最上
    绿联 电池
    阿里云
    Centos 8 搭建时钟服务器
    CentOS8系统时间同步解决方法
    解决问题的人干活快的人
    【海通国际】Joe Lowry(Mr. Lithium)谈全球电池原材料供应危机
    Linux 实验楼
    用 set follow-fork-mode child即可。这是一个 gdb 命令,其目的是告诉 gdb 在目标应用调用fork之后接着调试子进程而不是父进程,因为在 Linux 中fork系统调用成功会返回两次,一次在父进程,一次在子进程
    【随笔】阿里云修改DNS
  • 原文地址:https://www.cnblogs.com/james111/p/6995736.html
Copyright © 2011-2022 走看看