List阅读

List:

一种线性的数据结构,在Java中,List是一个接口,在接口中定义了相关的操作,它的父类是Collection接口,它的子类有:quentialListArrayListAttributeListCopyOnWriteArrayListLinkedListRoleListRoleUnresolvedListStackVector.

常用的List的子类有:ArrayList,LinkList,CopyOnWriteArrayList,Stack,Vector。

ArrayList:

一种低层以数组的方式存储数据的数据结构   。对于数组的优点是可以在常数级别实现对数据元素的访问,而对于数据的插入,删除,则需要将数组中的元素进行移动。

ArrayList中包含的重要常量有:(以下常量和方法都是来自JDK1.8中源代码中)

serialVersionUID 序列号,用于传输;

DEFAULT_CAPACITY:默认数组大小10;

Object[] EMPTY_ELEMENTDATA:数组对象,用于存储数据的数组;

Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA:默认空的数组对象。

    public ArrayList(int initialCapacity) {
	        if (initialCapacity > 0) {
	            this.elementData = new Object[initialCapacity];
	        } else if (initialCapacity == 0) {
	            this.elementData = EMPTY_ELEMENTDATA;
	        } else {
	            throw new IllegalArgumentException("Illegal Capacity: "+
	                                               initialCapacity);
	        }
	    }

即:使用特定参数的构造函数时,会创建一个数组,数组的大小为传入的指定参数的大小。如果为0,则空的数组,其他值则抛出异常。

    public ArrayList() {
	        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
	    }

即创建一个容量为10的list。

    public int indexOf(Object o) {
	        if (o == null) {
	            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中可以存储null对象,对于源代码中的indexOf,lastIndexOf,remove方法,需要做的是,先判断要传入的参数是否为null,如果为null,则对数组进行遍历,找出值为null的对象,如果不是空,则对数组进行遍历,使用equal方法进行判断,这里的o为Object对象,没有重写equal方法,所以equal方法还是Object类中的equal方法,使用对象的地址来判断是否属于同一个对象,而任何类都继承了Object类,对于不同的类,有的重写了equals方法,例如Integer类,因此对于不同的存储类,equals方法实现的方式不一样。为什么不直接使用equal方法,因为如果对象为null,null对象不存在equals方法,所以会抛出空指针异常错误。

 public boolean add(E e) {
        modCount++;
        add(e, elementData, size);
        return true;
    }

ArrayList向外部提供一个public类型的add方法,add方法的内部则调用私有化的add方法来进行add操作,这种手段有效的隐藏了真实的调用方法,私有化add方法:

private void add(E e, Object[] elementData, int s) {
        if (s == elementData.length)
            elementData = grow();
        elementData[s] = e;
        size = s + 1;
    }

首先判断数组的存放位置是否已经满了,如果没有,则将数据放在指定的位置,如果满了,则使用grow()进行扩充;

private Object[] grow() {
        return grow(size + 1);
    }
 private Object[] grow(int minCapacity) {
        return elementData = Arrays.copyOf(elementData,
                                           newCapacity(minCapacity));
    }

private int newCapacity(int minCapacity) {
        // overflow-conscious code
        int oldCapacity = elementData.length;
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        if (newCapacity - minCapacity <= 0) {
            if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA)
                return Math.max(DEFAULT_CAPACITY, minCapacity);
            if (minCapacity < 0) // overflow
                throw new OutOfMemoryError();
            return minCapacity;
        }
        return (newCapacity - MAX_ARRAY_SIZE <= 0)
            ? newCapacity
            : hugeCapacity(minCapacity);
    }

扩充是先将数组的大小加1,将其值传入minCapacity变量中,将其于Object数组的大小进行比较,如果大于Object数组值,则产生一个新的数组的容量值,值为当前数组值*1.5+1,如果值还是小于minCapacity,则将minCapacity作为新的容量值,然后使用Arrays.copyOf生成新的数组对象。

Arrays.copys方法会创建一个数组对象,然后将之前数组中的对象,复制到新的数组中。

注意:ArrayList不是线程安全的。

remove:

    public boolean remove(Object o) {
        final Object[] es = elementData;
        final int size = this.size;
        int i = 0;
        found: {
            if (o == null) {
                for (; i < size; i++)
                    if (es[i] == null)
                        break found;
            } else {
                for (; i < size; i++)
                    if (o.equals(es[i]))
                        break found;
            }
            return false;
        }
        fastRemove(es, i);
        return true;
    }

即先判断对象是否为null,为null,则遍历数组找到null,调用fastRemove来删除null对象,不为null,在使用equals()比较找到位置,然后调用fastRemove()方法来进行删除,fastRemove将index后的对象向前复制一位,并将数组中的最后一个元素设置为null.

LinkedList:

是一种低层采用链表的方式实现,链表为双向链表,即每个节点既有它的后继节点,又有它的前驱节点。

 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;
        }
    }

Node类为链表每个节点的框架。

LinkedList中的重要常量为:

Node<E> first: 第一个节点;

Node<E> last:最后一个节点;

int size : 节点的数量;

  public LinkedList() {
    }

创建一个空的链表;

 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的add方法会调用linkLast方法,linkLast会将根据新的值创建一个新的Node对象,然后判断last节点是否为null,如果为null,则说明链表为null,则将节点赋值给first节点,如果不为空,则将节点添加到last节点的后面。

    public boolean remove(Object o) {
        if (o == null) {
            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;
    }

remove方法同理,也是先判断对象是否为null,根据对象的值,进行不同操作的删除,和ArrayList同理。只不过删除的方法不一样,LinkList使用unlink(x)方法进行删除,

    E unlink(Node<E> x) {
        // assert x != null;
        final E element = x.item;
        final Node<E> next = x.next;
        final Node<E> prev = x.prev;

        if (prev == null) {
            first = next;
        } else {
            prev.next = next;
            x.prev = null;
        }

        if (next == null) {
            last = prev;
        } else {
            next.prev = prev;
            x.next = null;
        }

        x.item = null;
        size--;
        modCount++;
        return element;
    }

从中我们可以看出,first是链表的第一个节点,如果prev等于null,即表示当前节点为第一个节点,则将first指向next节点,即可删除x节点,如果prev不为null,则将prev.next指向x.next即可,next如果为空,则表示节点为最后一个元素,则将prev设置为last,即可删除,否则,将next.prev指向prev即可。

  public E get(int index) {
        checkElementIndex(index);
        return node(index).item;
    }

    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;
        }
    }

get方法会先检查下标是否在合理的范围内,如果在,将调用node(index)方法,然后node(index)将当前的index与链表的长度的一半进行比较,如果小于,则从前往后查找,如果大于,则从后往前查找。

Vector:

Vector和ArrayList一样,唯一的区别是Vector是线程安全的,他会在add,remove,get方法上使用synchronized关键字,来保证并发操作下的同步问题。

Stack:

栈是一种先进后出的数据结构,栈的实现可以数组,也可以使用链表,Java中的栈继承Vector类,Vector则低层则使用数组进行存储,所以栈使用数组进行存储。

栈的主要方法有:push,pop,peek,search,这些方法都是同步的,有关键字synchronized修饰,所以stack是线程安全的。

CopyOnWriteArrayList

CopyOnWriterArrayList是一个线程安全的,读操作无锁的ArrayList;

public CopyOnWriteArrayList() {
        setArray(new Object[0]);
    }
 final void setArray(Object[] a) {
        array = a;
    }

CopyOnWriteArrayList()构造器是一个创建一个大小为0的数组;

   public boolean add(E e) {
        synchronized (lock) {
            Object[] elements = getArray();
            int len = elements.length;
            Object[] newElements = Arrays.copyOf(elements, len + 1);
            newElements[len] = e;
            setArray(newElements);
            return true;
        }
    }

add方法是同步的,使用synchronized关键字,其添加方法和ArrayList方法一致。

  public boolean remove(Object o) {
        Object[] snapshot = getArray();
        int index = indexOf(o, snapshot, 0, snapshot.length);
        return (index < 0) ? false : remove(o, snapshot, index);
    }

private boolean remove(Object o, Object[] snapshot, int index) {
        synchronized (lock) {
            Object[] current = getArray();
            int len = current.length;
            if (snapshot != current) findIndex: {
                int prefix = Math.min(index, len);
      ......
            return true;
        }
    }

remove方法也是同步的,但是与ArrayList删除元素的方式不一致,即它会创建一个数组大小为当前组数-1大小的数组,然后遍历数组,寻找待删除的元素,然后将之后的元素全部赋值给新的数组对象,并做引用切换,如果没有找到,则将当前的元素赋值给新的数组对象,最后处理最后一个元素,如果最后一个元素是要删除的对象,则将当前数组赋值为新的数组对象,如果不等于,则返回false;

get(int)方法是没有synchronized关键字修饰,即没有使用锁,所以有可能会读取到脏数据,但是效率高。

 

 

如有异议,敬请指出,谢谢,与君共勉。

 

 

 

 

 

 

 

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值