java提供的list

ArrayList

其内部使用Object[]数组来存储,默认长度为10,一次增长为原长度的1.5倍,当1.5倍超过int的最大值时为int的最大值,再大一点就报错OOM
ArrayList在add元素时,先检测容量是否够add,不够就扩容1.5倍,然后将元素加入数组中,长度+1,这里为做多线程的并发处理,在多线程进行添加时,可能出现一起扩容,或同时将元素加到一个位置上,导致异常或数据丢失.

public boolean add(E e) {
        ensureCapacityInternal(size + 1);  // 判断容量够不够用,不够就扩容
        elementData[size++] = e;//添加元素,长度加一
        return true;
    }

private void ensureCapacityInternal(int minCapacity) {
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {//是不是空数组
            minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);//是不是比原始的10要小
        }

        ensureExplicitCapacity(minCapacity);//扩容
    }

    private void ensureExplicitCapacity(int minCapacity) {
        modCount++;

        // overflow-conscious code
        if (minCapacity - elementData.length > 0)//需要扩容
            grow(minCapacity);
    }

private void grow(int minCapacity) {
        // overflow-conscious code
        int oldCapacity = elementData.length;
        int newCapacity = oldCapacity + (oldCapacity >> 1);//这里的oldCapacity >> 1就是原长度除以2,即扩容为原来长度的1.5倍
        if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity;
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            newCapacity = hugeCapacity(minCapacity);//检查是否大于最大值,这里可能会抛出oom
        // minCapacity is usually close to size, so this is a win:
        elementData = Arrays.copyOf(elementData, newCapacity);//将数组复制到新的数组中
    }

CopyOnWriteArrayList

写时复制,在add数据时,会有加锁处理,再进行先复制原数组,在对新数组进行添加操作,最后在指向新数组,以此保证最终的一致性,但其不能保证数据读取的实时性,因为在写入时需要更多的时间了.

public boolean add(E e) {
        synchronized (lock) {//加锁,此处有些版本使用ReentrantLock锁
            Object[] elements = getArray();//获取原数组
            int len = elements.length;//原数组长度
            Object[] newElements = Arrays.copyOf(elements, len + 1);//复制原数组且长度+1
            newElements[len] = e;//新数组加入元素
            setArray(newElements);//指向新的数组,即替换原数组
            return true;
        }
    }

由此可见,写时复制列表的数组扩容每次扩容长度为+1,使用锁的机制来保证同一时刻只有一个在写,从而保证数组最终的一致性,且能保证在写的时候,原数据依然可以读取,但其读取的是旧数据.
当然由于复制数组,会造成更多的内存占用,可能导致gc频繁会gc时间较长,所以使用与读多写少的地方

Vector

向量列表,与ArrayList类似,在初始化的时候可设置其初始大小,默认为10,但每次扩容的长度可在初始化时设置,若不设置,默认增长为原数组的两倍.除此之外,Vector中的方法都进行了同步操作,一次解决多线程的并发问题,但由于都进行了同步,所以效率会比其他的列表低.

public synchronized boolean add(E var1) {//整个方法都同步
        ++this.modCount;
        this.ensureCapacityHelper(this.elementCount + 1);//扩容判断
        this.elementData[this.elementCount++] = var1;//存入元素
        return true;
    }
public synchronized E get(int var1) {//读的时候也是整个方法都同步
        if(var1 >= this.elementCount) {//越界直接抛异常
            throw new ArrayIndexOutOfBoundsException(var1);
        } else {
            return this.elementData(var1);
        }
    }

Stack

继承与Vector的栈,其中的方法也是同步的,符合栈数据结构的先进后出,使用push()入栈,pop()出栈,peek()获取栈顶元素但不出栈,search()搜索对应数据在栈中的位置,empty()判断是否0元素

IdentityStack

与Stack一样,但其不同之处在于相等的判断,Stack相等的判断使用equls(),而IdentityStack使用==,即IdentityStack为对象相等,Stack为值相等
IdentityStack中为:

public synchronized int indexOf(Object o, int pos) {
        for (int i = pos; i < size(); i++) {
            if (get(i) == o) {//对象相等
                return i;
            }
        }
        return -1;
    }

Stack中的判断使用了Voter的判断,为

public synchronized int lastIndexOf(Object o, int index) {
        if (index >= elementCount)
            throw new IndexOutOfBoundsException(index + " >= "+ elementCount);

        if (o == null) {
            for (int i = index; i >= 0; i--)
                if (elementData[i]==null)
                    return i;
        } else {
            for (int i = index; i >= 0; i--)
                if (o.equals(elementData[i]))//值相等
                    return i;
        }
        return -1;
    }

LinkedList

链表结构的列表, 其中特有的方法:addFirst(E e) ,addLast(E e) ,getFirst() ,getLast() ,removeFirst(),removeLast()
还提供了栈结构的实现与队列结构的实现
栈:先进后出push() ,pop()
队列:先进先出offer(),poll()
其插入删除速度会比数组的快很多,其中没有做并发处理

小结

java提供了不同的list,开发者需要根据应用场景来选择合适的list,虽然有些可能写错了,希望大家指出纠正

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章