Linkedlist &Arraylist总结

Arraylist

public class ArrayList<E> extends AbstractList<E>
        implements List<E>, RandomAccess, Cloneable, java.io.Serializable

在这里我们看到,Arraylist 的父类是AbstractList 实现的接口是List ,RandomAccesss ,Clonable 。这就表示,Arraylist 具有List 的基本框架和功能,可实现快速访问,可以克隆。

private static final int DEFAULT_CAPACITY = 10;
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);
    }
}

在这里我们可以看到,默认的大小是10 ,如果有传入大小(initialCapacity),就使用传入的大小,不过传入的小要合法,不能小于0 ,等于0是依然使用默认大小。

private static final Object[] EMPTY_ELEMENTDATA = {};
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

在这里我们看到,定义两个静态数组,这也是Arraylist 的本质。意思就是说,Arraylist 是基于数组实现的。

基于数组实现的有哪些好处呢?
数组是计算机内部开辟出的一块连续的内存,所,我们在寻址操作时,消耗的是常数时间,但是在数据的添加和删除操作时,所消耗的时间可能会是很大的,这取决与数组的大小了操作数据的位置。

那为什么不直接使用数组呢?
1.我们知道,数组是一种数据结构,而Arraylist 是Java集合里的类。我们在使用Arraylist调用他下面的数组是,都是“.”操作,这是引用,而我们知道,Java的GC来说,当一个数据没有应用的时候,就会被回收,但是,对于基本数据结构 数组来说,我还不知道是怎么消除的。这是我猜的。。。。
2. 其实,就我目前的水平来说,看中的还是Arraylist 的数组是动态的,还有一些优秀的方法。
我们来看扩容:

private void grow(int minCapacity) {
    // overflow-conscious code
    int oldCapacity = elementData.length;
    int newCapacity = oldCapacity + (oldCapacity >> 1);
    if (newCapacity - minCapacity < 0)
        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);
}

private static int hugeCapacity(int minCapacity) {
    if (minCapacity < 0) // overflow
        throw new OutOfMemoryError();
    return (minCapacity > MAX_ARRAY_SIZE) ?
        Integer.MAX_VALUE :
        MAX_ARRAY_SIZE;
}

在最小容量小进行扩容,先记录原数组大小,然后
int newCapacity = oldCapacity + (oldCapacity >> 1);这是关键一步,新的容量等于旧的容量+旧的容量右移一位。结果就是数组被扩大了原来的1.5倍。后面,就是进行一系列的判断,目的就是不让新数组过大或者过小。
这里是拷贝方法:

public Object clone() {
    try {
        ArrayList<?> v = (ArrayList<?>) super.clone();
        v.elementData = Arrays.copyOf(elementData, size);
        v.modCount = 0;
        return v;
    } catch (CloneNotSupportedException e) {
        // this shouldn't happen, since we are Cloneable
        throw new InternalError(e);
    }
}

我们可以看到关键步骤是:
v.elementData = Arrays.copyOf(elementData, size);V的数据有Arrays.copyOf()得到。这里是Arrays.copyOF()方法;
这里要返回新的数组对象。

public static <T> T[] copyOf(T[] original, int newLength) {
    return (T[]) copyOf(original, newLength, original.getClass());
}

ruturn 的copyof()方法是这个:

public static <T,U> T[] copyOf(U[] original, int newLength, Class<? extends T[]> newType) {
    @SuppressWarnings("unchecked")
    T[] copy = ((Object)newType == (Object)Object[].class)
        ? (T[]) new Object[newLength]
        : (T[]) Array.newInstance(newType.getComponentType(), newLength);
    System.arraycopy(original, 0, copy, 0,
                     Math.min(original.length, newLength));
    return copy;
}

而在这个copyof()中,使用的是,System.arraycopy()函数;

public static native void arraycopy(Object src,  int  srcPos,
                                    Object dest, int destPos,
                                    int length);

其中 src 原数组
srcPos 原数组起始位置
dest 目标数组
destPos 目标数组其实位置
length 要拷贝的长度

LinkedList

public class LinkedList<E>
    extends AbstractSequentialList<E>
    implements List<E>, Deque<E>, Cloneable, java.io.Serializable

这里我们看到LinkedList 继承的父类是AbsractSequentialList,实现的接口是List,Deque,cloneable ,io.Serializable

public abstract class AbstractSequentialList<E> extends AbstractList<E> {

我们看到AbstractqueuenialList的父类是AbbstractList 。 AbstractqueueialList主要的不同就是,它不像AbstractList那样,支持随机访问,它只能顺序访问。
其他的与Arraylist 不同的是Deque(堆) & Serializable (可序列化操作)
头结点:

transient Node<E> first;

尾结点:

transient Node<E> last;

我们看到这里是节点Node 类型。

/一个静态内部类实现
private static class Node<E> {
    E item;//存储的数据
    Node<E> next;//下一个节点
    Node<E> prev;//前一个节点
//构造函数,实现一个Node.
    Node(Node<E> prev, E element, Node<E> next) {
        this.item = element;
        this.next = next;
        this.prev = prev;
    }
}

也就说,LainkedList 是基于双向链表实现的。我们知道,对于链表的优势就是,由于数据地址不是连续的,添加/删除元素都是通过对节点进行操作。这是很方便的。但是,当要遍历元素时,所有耗费的时间就是巨大的。当然,也会有优化。因为是链表实现,所以就没有默认大小。
这里是添加元素:

public boolean add(E e) {
    linkLast(e);
    return true;
}

这里可以看到使用linkLast();方法添加。我们可以看到,默认是尾添加。
这里是linkedLast()方法;

void linkLast(E e) {
    final Node<E> l = last;//不可改变节点l
    final Node<E> newNode = new Node<>(l, e, null);//new一个节点,传入l 数据e 和一个null空指针??
    last = newNode;//将新节点给last
    if (l == null)//如果l等于空值,就将新节点给头结点
        first = newNode;
    else
        l.next = newNode;//否则 ,让新节点赋给last的下一个节点
    size++;//链表大小加1
    modCount++;//对数据结构操作次数加1  在迭代器中使用
}

这里是删除操作:

public E remove() {
    return removeFirst();
}

这里使用removeFirst()方法删除元素。我们可以看到,默认头删除。
这里是removeFirst()方法;

public E removeFirst() {
    final Node<E> f = first;
    if (f == null)
        throw new NoSuchElementException();
    return unlinkFirst(f);
}

这里就没有删除,就实现了一个异常判断,所要删除的节点是否是null。
然后 返回 unlinkeFirst();方法,具体代码如下:

//这里传入的是节点
private E unlinkFirst(Node<E> f) {
    // assert f == first && f != null;
    final E element = f.item;//将传入节点的数据给element
    final Node<E> next = f.next;//传入节点的next给Node的next;
    f.item = null;//将f的值赋空
    f.next = null; // help GC  让GC帮忙回收
    first = next;//将next赋值给first节点
    if (next == null)
        last = null;//如果next为空,就让last等于null 表示,就一个节点。
    else
        next.prev = null;//如果不是,就将新的头结点的前一个节点设为null
    size--;//链表大小减1
    modCount++;//操作次数加1
    return element;
}

图片演示:在这里插入图片描述
当然,LinkedList 也有很多优秀的方法。比如clone()方法;

public Object clone() {
    LinkedList<E> clone = superClone();

    // Put clone into "virgin" state
    clone.first = clone.last = null;
    clone.size = 0;
    clone.modCount = 0;

    // Initialize clone with our elements
    for (Node<E> x = first; x != null; x = x.next)
        clone.add(x.item);

    return clone;
}

该方法中,最主要是for (Node<E> x = first; x != null; x = x.next) clone.add(x.item);这一段。遍历链表,直到X==null。
通过clone.add()方法进行追加。

public boolean add(E e) {
    linkLast(e);
    return true;
}

这个方法在上面分析过,就不多说了。

总结

Arraylist是基于数组实现的,具有很快的查询能力,但是在删除和添加操作时,需要耗费巨大的时间 (因为移动元素)。
Arraylist 支持随机访问,而且可以实现动态数组,以1.5倍的形式扩容。
LinkedList 是基于链表实现的。在添加和删除操作时,很方便,但是,它的遍历就很麻烦。哦,这里忘说了优化查找问题。在指定位置查找时,它会判断输入的index是更靠近与那一端,如果是尾,就会倒着遍历,当然,靠近头就顺着遍历(因为是双链表)。
LinkedList 不支持随机访问,但是可以序列化操作。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值