JAVA 容器,LinkedList、arraylist 源码分析。容器、集合类对比,arraylist、vector、

List 与Set接口

Collection 接口存储一组不唯一、无序的对象

List接口存储一组不唯一、有序(插入顺序)的对象

Set接口存储一组唯一、无序的对象

Map接口存储一组键值对象,提供key到value的映射

set接口中的实现类

HashSet:采用Hashtable哈希表存储结构

优点:添加速度快,查询速度快,删除速度快

缺点:无序

TreeSet

采用二叉树(红黑树)的存储结构

优点:有序(排序后的升序)查询速度比List快

缺点:查询速度没有HashSet快

java 容器 collection 和map的区别。collection中存储了一组对象,而map存储的是key、value,并且key不能重复。

Map:

HashMap:非线程安全、无序、key不重复。

TreeMap:非线程安全、有序、key不重复。

Hashtable:线程安全、无序、不重复。

ConcurrentHashMap:线程安全,有序、不重复。

Collection

set:  有序、不重复对象

TreeSet:非线程安全、有序、不重复。

HashSet:非线程安全、有序、不重复。

List:  按插入顺序、对象可重复

TreeList:非线程安全、按插入顺序、可重复。

ArrayList:非线程安全、插入顺序、可重复。

LinkedList:非线程安全、插入顺序、可重复。

Vector:线程安全、插入顺序、可重复。

Arraylist : 初始大小为10,原大小+ 原大小右移1位 = 1.5 扩容。oldCapacity + (oldCapacity >> 1)。插入删除时都会copy一份。

Vector: 直接2倍扩容。oldCapacity + ((capacityIncrement > 0) ? capacityIncrement : oldCapacity)

HashMap: 初始16 ,2倍(2的n次幂)扩容.左移1位.newCap = oldCap << 1

为什么hashmap容量是2的n次幂?

1.方便进行 与 运算 (HashMap: 376行),与运算比取模运算效率高

2.在扩容之后涉及到元素的迁移过程,迁移的时候只需要判断二进制的前一位是0或者1即可。

   如果是0,表示新数组和旧数组的下标位置不变,如果是1,只需要将索引位置加上旧的数组的长度值即为新数组的下标


ArrayList 源码分析

add(Object o )

/**
     * 将当前数组大小+1传入 ensureCapacityInternal方法
     * 将e 对象添加到 elementData 数组的 当前数组+1位置。数据的最后一位,非数组的末尾!!!
     * 并且每次添加对象,数组的大小都会1.5倍扩张
     */
    public boolean add(E e) {
        ensureCapacityInternal(size + 1);  // 当前数组大小 + 1
        elementData[size++] = e;
        return true;
    }

    /**
     * 判断下 elementData 是否是空的,空的就取最小值10
     * 如果当前数组+1 的大小大于10,就取当前数组+1
     * @param minCapacity
     */
    private void ensureCapacityInternal(int minCapacity) {
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) { // 如果这个数组是空的
            minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);  //初始化一个 10 的大小,或者数组+1 个
        }

        ensureExplicitCapacity(minCapacity);
    }
    public static int max(int a, int b) {//a=10 ,b=当前数组大小
        return (a >= b) ? a : b;
    }

    /**
     * 如果当前数组+1  大于  elementData的length 执行grow
     * @param minCapacity
     */
    private void ensureExplicitCapacity(int minCapacity) {
        modCount++;

        // overflow-conscious code
        if (minCapacity - elementData.length > 0) //一般是true。当前数组+1 大于 当前数组大小
            grow(minCapacity);
    }
    /**
     * 先获取 elementData 的length,然后*1.5倍。如果这个值比当前数组+1 小,就取当前数组+1 minCapacity
     * 如果 newCapacity 比 最大值大,就取 最大值或者 最大值减8
     * 将 elementData 数组扩大至 当前数组的 1.5 倍
     */
    private void grow(int minCapacity) {
        // overflow-conscious code
        int oldCapacity = elementData.length; //当前数据的大小
        int newCapacity = oldCapacity + (oldCapacity >> 1); // 1.5 倍。右移运算,不懂的可以for循环打出,数组新值
        if (newCapacity - minCapacity < 0) //如果 新值 小于当前数组+1
            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);
    }

    /**
     * 当前数组+1 大于最大值 就返回 integer最大值,否则返回integer最大值-8
     */
    private static int hugeCapacity(int minCapacity) {
        if (minCapacity < 0) // overflow
            throw new OutOfMemoryError();
        return (minCapacity > MAX_ARRAY_SIZE) ?
                Integer.MAX_VALUE :
                MAX_ARRAY_SIZE;
    }

通过源码可以看到,arraylist 添加对象的时候,初始大小是10,然后会进行每次1.5倍的扩张。

而且添加的时候,是将现有的数组进行copy一份的。所以插入效率不高。

remove(object o)

/**
     * 先进行遍历,获取到要删除的位置的索引。
     * 然后调用系统的删除方法
     */
    public boolean remove(Object o) {
        if (o == null) {
            for (int index = 0; index < size; index++)
                if (elementData[index] == null) {
                    fastRemove(index);
                    return true;
                }
        } else {
            for (int index = 0; index < size; index++)
                if (o.equals(elementData[index])) {
                    fastRemove(index);
                    return true;
                }
        }
        return false;
    }

    /**
     * 进行copy删除
     */
    private void fastRemove(int index) {
        modCount++;
        int numMoved = size - index - 1;
        if (numMoved > 0)
            System.arraycopy(elementData, index+1, elementData, index,
                    numMoved);
        elementData[--size] = null; // clear to let GC do its work
    }

可以发现,remove效率也不高,先遍历再copy一份删除。

LinkedList

/**
     * 先要取出next,然后再取出next的item进行比较
     */
    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;
    }

    /**
     * 直接链到最后面
     */
    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++;
    }

插入效率高、删除速度快。遍历和随机访问效率低。

为什么说linkedlist是双向链表?

因为在linkedlist中存储的Node,会记录上一个下一个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;
        }
    }

Vector

add: 添加时进行当前大小的*2扩增,并且是线程安全的。

public synchronized boolean add(E e) {
        modCount++;
        ensureCapacityHelper(elementCount + 1);
        elementData[elementCount++] = e;
        return true;
    }
    private void ensureCapacityHelper(int minCapacity) {
        // overflow-conscious code
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }

    /**
     * newCapacity = oldCapacity + ((capacityIncrement > 0) ? capacityIncrement : oldCapacity)
     * 两倍扩增
     */
    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);
    }

 

Queue 和 list的区别

Queue 和list 的区别。queue 添加了很多对线程友好的api  : offer、peek、poll。 put、take 会阻塞

Queue :当队列满了的时候调用 add方法会抛出异常,同理remove也会抛出异常。

queue提供了offer、peek、poll的支持。

offer 添加,返回值boolean,不会报错

peek 返回队列的第一个(头)元素,不会删除

poll 返回队列头元素并删除

ArrayBlockingQueue  实现了 BlockingQueue, BlockingQueue 继承自Queue

ArrayBlockingQueue  有take方法,得到头元素并删除,如果queue是空的就进行等待(阻塞),需要捕获异常

ArrayBlockingQueue  有put方法,添加时如果满了就进行等待(阻塞),需要捕获异常。

ArrayBlockingQueue  是线程安全的,方法里面加入了 ReentrantLock 

Arrays

用于对数组操作的工具类

java.util.Collections  类!!  而不是接口 Collection

这个类是专门对实现了List的类提供了一些静态方法支持,以及提供了将非同步Set、List、Map转换成同步的方法

sort()排序:  Collections.sort(list);

public static <T extends Comparable<? super T>> void sort(List<T> list) {
        list.sort(null);
    }

如果是自定义的对象,需要实现 Comparable 接口

binarySearch : 对半查找

//很明显会调用 iteratorBinarySearch
    public static <T>
    int binarySearch(List<? extends Comparable<? super T>> list, T key) {
        if (list instanceof RandomAccess || list.size()<BINARYSEARCH_THRESHOLD)
            return Collections.indexedBinarySearch(list, key);
        else
            return Collections.iteratorBinarySearch(list, key);
    }

    /**
     * 这里使用了一个无符号右移(逻辑右移) int mid = (low + high) >>> 1;
     * 上面一行的 运算结果 为((low + high)) 的一半
     * 所以这个查找也叫对半查找
     */
    private static <T>
    int iteratorBinarySearch(List<? extends Comparable<? super T>> list, T key)
    {
        int low = 0;
        int high = list.size()-1;
        ListIterator<? extends Comparable<? super T>> i = list.listIterator();

        while (low <= high) {
            int mid = (low + high) >>> 1;
            Comparable<? super T> midVal = get(i, mid);
            int cmp = midVal.compareTo(key);

            if (cmp < 0)
                low = mid + 1;
            else if (cmp > 0)
                high = mid - 1;
            else
                return mid; // key found
        }
        return -(low + 1);  // key not found
    }

    //遍历查找
    private static <T> T get(ListIterator<? extends T> i, int index) {
        T obj = null;
        int pos = i.nextIndex();
        if (pos <= index) {
            do {
                obj = i.next();
            } while (pos++ < index);
        } else {
            do {
                obj = i.previous();
            } while (--pos > index);
        }
        return obj;
    }

shuffle:随机排序

public static void shuffle(List<?> list) {
        Random rnd = r;
        if (rnd == null)
            r = rnd = new Random(); // harmless race.
        shuffle(list, rnd);
    }

reverse:倒序

public static void reverse(List<?> list) {
        int size = list.size();
        if (size < REVERSE_THRESHOLD || list instanceof RandomAccess) {
            for (int i=0, mid=size>>1, j=size-1; i<mid; i++, j--)
                swap(list, i, j);
        } else {
            // instead of using a raw type here, it's possible to capture
            // the wildcard but it will require a call to a supplementary
            // private method
            ListIterator fwd = list.listIterator();
            ListIterator rev = list.listIterator(size);
            for (int i=0, mid=list.size()>>1; i<mid; i++) {
                Object tmp = fwd.next();
                fwd.set(rev.previous());
                rev.set(tmp);
            }
        }
    }

copy:从一个集合复制到另一个集合

public static <T> void copy(List<? super T> dest, List<? extends T> src) {
        int srcSize = src.size();
        if (srcSize > dest.size())
            throw new IndexOutOfBoundsException("Source does not fit in dest");

        if (srcSize < COPY_THRESHOLD ||
            (src instanceof RandomAccess && dest instanceof RandomAccess)) {
            for (int i=0; i<srcSize; i++)
                dest.set(i, src.get(i));
        } else {
            ListIterator<? super T> di=dest.listIterator();
            ListIterator<? extends T> si=src.listIterator();
            for (int i=0; i<srcSize; i++) {
                di.next();
                di.set(si.next());
            }
        }
    }

 

猜你喜欢

转载自blog.csdn.net/dandanforgetlove/article/details/106029923