集合---List

★ 

List下面最常用的实现类ArrayList,底层使用数组存放内容,重要掌握如何初始化空间,如何扩容等等。

一,ArrayList

1.字段

private static final int DEFAULT_CAPACITY = 10;  //默认初始化大小 10
private static final Object[] EMPTY_ELEMENTDATA = {};   //这两个字段是用来区分构造方式的,是默认构造还是固定了参数
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
transient Object[] elementData; // non-private to simplify nested class access  //存放元素的数组
private int size;  //实际存储元数的个数
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;  //数组能达到的最大值。

注意底层存在的是Object对象,其次注意初始化大小是10.  注意最大值是integer.Max_value-8

2.构造方法

2.1 带大小的构造方法

public ArrayList(int initialCapacity) {
    if (initialCapacity > 0) {
        this.elementData = new Object[initialCapacity];
    } else if (initialCapacity == 0) {
        this.elementData = EMPTY_ELEMENTDATA;
    } ...
}

两个方式,如果初始化的大小>0,那么就直接new出来一个Object数组。

如果==0,那么直接使用默认的空元数。

2.2 无参构造:

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

两个空集合在初始化在add()数据判断容量的时候初始化。

2.3 集合构造:

public ArrayList(Collection<? extends E> c) {
    elementData = c.toArray();
    if ((size = elementData.length) != 0) {
        // c.toArray might (incorrectly) not return Object[] (see 6260652)
        if (elementData.getClass() != Object[].class)
            elementData = Arrays.copyOf(elementData, size, Object[].class);
    } else {
        // replace with empty array.
        this.elementData = EMPTY_ELEMENTDATA;
    }
}

将集合转成数组,如果容量不等于0,那么就使用工具类Arrrays复制过来。

如果等于0,那么就等同于参数为0的构造方法。

3.add()

添加元素,要确保容器的空间足够才能添加元素,如果空间不足,那么这里就涉及到了扩容机制。

为什么最大值等于Integer.Max_Value-8呢?

* The maximum size of array to allocate.
* Some VMs reserve some header words in an array.
* Attempts to allocate larger arrays may result in
* OutOfMemoryError: Requested array size exceeds VM limit
*/
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

从注释中翻译过来,是要留一部分空间给header words,如果尝试获取更大内存,会造成oom错误。

为什么是Int,也可以这样理解,在操作方法中get,set,index里面的参数都是int,所以这里对于的也是Integer的最大值。其次-8,涉及到了数组在jvm中的分配方式,它需要一个数组头来表示数组的长度,64位的jvm中这个头的长度就是8。

3.1 add()

public boolean add(E e) {
    ensureCapacityInternal(size + 1);  // Increments modCount!!
    elementData[size++] = e;
    return true;
}

空间是否可行判断:

private void ensureCapacityInternal(int minCapacity) {
    ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}

传入参数,目前的元素+1,代表加入一个元素的大小空间,这里还有一个入口才到达内容,入口中调用了一个方法,calculate()

private static int calculateCapacity(Object[] elementData, int minCapacity) {
    if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
        return Math.max(DEFAULT_CAPACITY, minCapacity);
    }
    return minCapacity;
}

    先判断容器是否是0空容器,如果是呢,就需要判断新增加的大小是否超过了10,没有就直接把空间扩容到10.否者返回实际空间大小。

private void ensureExplicitCapacity(int minCapacity) {
    modCount++; //被修改次数,这个字段在父类AbstractList中
    // overflow-conscious code
    if (minCapacity - elementData.length > 0)
        grow(minCapacity);
}

这里也是一个入口,调用grow()方法

private void grow(int minCapacity) {
    // overflow-conscious code
    int oldCapacity = elementData.length;
    int newCapacity = oldCapacity + (oldCapacity >> 1);  //新的大小,x+x/2
    if (newCapacity - minCapacity < 0)  //与所需大小比较,如果装不下,就直接采用所需大小
        newCapacity = minCapacity;
    if (newCapacity - MAX_ARRAY_SIZE > 0) //溢出了,判断所需大小是否超过max,没超过就直接采用max
        newCapacity = hugeCapacity(minCapacity);
    // minCapacity is usually close to size, so this is a win:
    elementData = Arrays.copyOf(elementData, newCapacity);  //扩容
}

如果所需大小没有超过max值,那么直接采用max值。

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

3.2 add(index,value)

添加元数到指定位置,这里涉及的就是元素的移动问题。

public void add(int index, E element) {
    rangeCheckForAdd(index); //判断index是否合法
    ensureCapacityInternal(size + 1);  // Increments modCount!!
    System.arraycopy(elementData, index, elementData, index + 1,size - index);
    elementData[index] = element;
    size++;
}

4.get()

在素组中,可以随机获取元素,实现逻辑比较简单。

public E get(int index) {
    rangeCheck(index);//判断 index是否合法
    return elementData(index); //
}

范型的强制转换

E elementData(int index) {
    return (E) elementData[index]; //直接数组随机获得
}

5.remove()

根据Index删除元素,返回该元素,首先就是要get()到,其次就是移动数组。

public E remove(int index) {
    rangeCheck(index); //检测index是否合法
    modCount++;     //修改次数
    E oldValue = elementData(index); //取得元素,返回
    int numMoved = size - index - 1;  //获得移动的数量
    if (numMoved > 0)  //移动后面的元素,向前移动
        System.arraycopy(elementData, index+1, elementData, index,numMoved);
    elementData[--size] = null; // 清除最后一个元素。并且size--
    return oldValue;
}

还有一种删除指定元数的,这个就需要先找到元素,(可能还存在重复值,默认应该是从头到尾找到的第一个元素)

public boolean remove(Object o) {
    if (o == null) { //传入的是null,则使用==来判断 否者使用equals来判断
        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;
}

对于其中的fastRemove()就和上面的move(index)内容差不多了。

6.获取指定元素的下标

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

 

二,LinkedList

1.底层实现

transient int size = 0;
transient Node<E> first; //指向链表首部的指针
transient Node<E> last;  //指向链表尾部的指针

双向链表节点,同时这三个字段,也是LinkedList的所以字段。

节点的结构:内部类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;
    }
}

2.添加元数

普通的add(e)方法,调用的是添加到末尾。

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

在头部插入:

private void linkFirst(E e) {
    final Node<E> f = first;
    final Node<E> newNode = new Node<>(null, e, f); //疯转
    first = newNode; //插入完毕
    if (f == null)
        last = newNode;
    else
        f.prev = newNode;
    size++;
    modCount++;
}

在构建的时候直接插入。

在尾部插入:

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

在这里你就能发现Node类构造函数三个参数的巧妙使用。

在指定节点之前插入一个节点:

void linkBefore(E e, Node<E> succ) {
    // assert succ != null;
    final Node<E> pred = succ.prev; //获得指定节点的前继节点
    final Node<E> newNode = new Node<>(pred, e, succ);//插入新节点
    succ.prev = newNode; //更新指定节点的前继节点
    if (pred == null)
        first = newNode;
    else
        pred.next = newNode;
    size++;
    modCount++;
}

3.删除节点

默认的remove()方法删除的是指定节点:

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

删除头节点:

private E unlinkFirst(Node<E> f) {
    // assert f == first && f != null;
    final E element = f.item; //返回指定节点的值
    final Node<E> next = f.next; //获得后继节点
    f.item = null;
    f.next = null; // help GC  方便回收
    first = next;        //删除头节点
    if (next == null)
        last = null;
    else
        next.prev = null;
    size--;
    modCount++;
    return element;
}

同理存在删除尾节点,

删除指定节点

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

4.获得节点

获得头节点:

public E getLast() {
    final Node<E> l = last;
    if (l == null)
        throw new NoSuchElementException();
    return l.item;
}

 

三 vector

1.字段

protected Object[] elementData; //存放内容的容器
protected int elementCount;    //数据个数
protected int capacityIncrement;    //扩容增长系数

2.构造方法

public Vector() {
    this(10);
}
public Vector(int initialCapacity) {
    this(initialCapacity, 0);
}
public Vector(int initialCapacity, int capacityIncrement) {
    super();
    if (initialCapacity < 0)
        throw new IllegalArgumentException("Illegal Capacity: "+
                                           initialCapacity);
    this.elementData = new Object[initialCapacity];
    this.capacityIncrement = capacityIncrement;
}

注意默认的增长系数是0

3.添加元素

public synchronized boolean add(E e) {
    modCount++;
    ensureCapacityHelper(elementCount + 1);
    elementData[elementCount++] = e;
    return true;
}

扩容机制:如果扩容系数<1,那么直接翻倍。>0,则直接添加扩容系数值。

private void ensureCapacityHelper(int minCapacity) {
    if (minCapacity - elementData.length > 0)
        grow(minCapacity);
}
private void grow(int minCapacity) {
    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);
}
private static int hugeCapacity(int minCapacity) {
    return (minCapacity > MAX_ARRAY_SIZE) ?
        Integer.MAX_VALUE :
        MAX_ARRAY_SIZE;
}

4.获取元素

随机访问获取。

public synchronized E get(int index) {
    if (index >= elementCount)
        throw new ArrayIndexOutOfBoundsException(index);

    return elementData(index);
}

参考连接:

1.https://www.jianshu.com/p/6deae8f3e8d9

2.https://www.jianshu.com/p/20628d30b249

3.https://www.jianshu.com/p/e07d868bfa0d

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值