Java基础 之 ArrayList和LinkedList

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_34083066/article/details/86756367

ArrayList

Arraylist是基于数组实现的

 transient Object[] elementData;

elementData是ArrayList真正用来保存数据的Object数组。

构造器

一边我们使用时都是:

List<Object> arrayList = new ArrayList<>();

默认使用空构造器

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

    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

所以当我们新建一个ArrayList时都是一个空的Object数组。

这里出现两个疑问:

1,那么初始化一个空数组,那么怎么添加元素?

其实在我们可以使用其他构造器指定初始值的大小,比如:

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

如果我们不改变使用的构造器的话:

当我们向集合中添加元素时,他会自动扩容。后面会讲到。

2,既然是object数组那么泛型的是干什么呢?

其实这个地方我理解,泛型在arraylist集合中只是起到一个校验元素的作用。泛型我们知道并不是一个真正类型,当编译时还是会编译成具体类型。所以在集合中使用泛型其实对程序运行没有任何效率上的帮助,但是他会让我们可以一个集合只存储一种固定的类型。

 

add

    private static final int DEFAULT_CAPACITY = 10;
    
    public boolean add(E e) {
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        elementData[size++] = e;
        return true;
    }
    private void ensureCapacityInternal(int minCapacity) {
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
        }

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

其实很简单,简单一看就懂了。

添加一个元素时,默认添加到数组最后一个。

比较size+1和默认的10取大;如果容量不够了,调用grow方法。然后使用copy方法对其进行扩容。

再看一下add的另一个方法

    public void add(int index, E element) {
        rangeCheckForAdd(index);

        ensureCapacityInternal(size + 1);  // Increments modCount!!
        System.arraycopy(elementData, index, elementData, index + 1,
                         size - index);
        elementData[index] = element;
        size++;
    }

指定位置添加。我们这里看到他会对数组进行一次copy,所以这里出现了一个arraylist的缺点;

arraylist因为基于数组,所以如果是顺序添加按位查找或按顺序遍历,那么他的性能非常好。但是如果对其某一个位置的内容进行修改或者使用迭代器遍历,那么他的性能就会大打折扣,因为他要是进行数组的copy,并且操作的数据越靠前,他copy的数组越大,性能就越差。

remove方法也类似,这里就不多赘述了。

 

 

LinkedList

我们先看一下他的内部定义的几个成员变量

public class LinkedList<E>
    extends AbstractSequentialList<E>
    implements List<E>, Deque<E>, Cloneable, java.io.Serializable
{
    transient int size = 0;

    /**
     * Pointer to first node.
     * Invariant: (first == null && last == null) ||
     *            (first.prev == null && first.item != null)
     */
    transient Node<E> first;

    /**
     * Pointer to last node.
     * Invariant: (first == null && last == null) ||
     *            (last.next == null && last.item != null)
     */
    transient Node<E> last;
    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;
        }
    }

很明显能看出来linkedlist内部实现是使用node内部类进行存储数据的。他有两个node,一个是first,一个是last,linkedlist保存这两个node,其他的node之间的关系,要看node内部去维护。

node有3个属性:

item:要存的值
prev:前一个
next:下一个

所以数据结构就很明白了。他是一个链表时的结构。

 

add方法

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

他先讲last备份,然后将要存的值创建一个node对象进行保存,并且将之前的last值的关系存入node,这个新的node也就是最后一个了。然后判断如果之前的last为空,说明这是第一个元素。如果不是那么将之前备份的那个元素的next指针指向新的node对象。

由这个方法我们看出来linkedlist的特性:

linkedlist也是自动保存到最后一位。他的值是以node对象的形式存储的,相对于arraylist来说更占一些内存。但是他无需扩容,并且不用copy数组,所以性能相对较高。他适合于迭代器来遍历,适用于按位添加。

 

 

 

这里说明一下我看的源码的JDK版本是1.8,每个版本的jdk的实现都不太一样。略有区别。但暴露的API实现功能还是一致的。

猜你喜欢

转载自blog.csdn.net/qq_34083066/article/details/86756367
今日推荐