ArrayList、LinkedList、Vector的联系和区别

ArrayList

继承了AbstractList类,实现了 List 接口。底层基于数组,可以实现容量大小动态变化。允许 null 的存在。同时实现了 RandomAccess接口(提供随机访问支持。遍历元素的方法)、Cloneable接口(支持被克隆)、Serializable接口(支持序列化),所以ArrayList 是支持快速随机访问、克隆、序列化的。但是ArrayList不是线程安全的,可以应用于无需保证线程安全的时候使用,在需要考虑线程安全的多线程环境下可以使用CpoyOnWriteArrayList。
构造方法

//无参构造
public ArrayList() {
    this.elementData = 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);
    }
}
//参数为集合的构造器
public ArrayList(Collection<? extends E> c) {
    elementData = c.toArray();//将Collection转换为数组,并赋值给elementData
    if ((size = elementData.length) != 0) {//获取元素个数并赋值给size属性
        if (elementData.getClass() != Object[].class)//如果是对象数组则执行数组的拷贝操作
            elementData = Arrays.copyOf(elementData, size, Object[].class);
    } else {
        this.elementData = EMPTY_ELEMENTDATA;
    }
}

成员属性

  • private static final int DEFAULT_CAPACITY = 10; 默认数组元素的初始化容;
  • private static final Object[] EMPTY_ELEMENTDATA = {}; 没有元素的空对象数组,没有存储任何元素,数组长
    度为 ;
  • private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {}; 构建 ArrayList 时默认使用的
    空数组。
  • transient Object[] elementData; 就是用于存放元素的数组
  • private int size; 真实存放的元素个数

实例

public static void main(String[] args) {
    ArrayList list = new ArrayList();
    for (int i = 0; i < 10; i++) {
        list.add(i);
    }
    list.forEach(i -> System.out.print(i + "\t"));
}

ArrayList的底层结构是通过数组保存元素数据的,当创建默认构造器,则默认容积为10,但是是采用延迟构建。
当容积不够是,会自动进行扩容,每次扩容为原始容积的50%。
ArrayList克隆操作是将全部元素克隆到一个新数组中。
ArrayList实现序列化是,应先写出容积,再一次写出每个元素。

主要方法

add()

//ArrayList在添加元素是,首先进行index范围检查,防止传入的index值不在范围内,在和默认容积大小值进行比对,如果大于默认值则需要进行扩容处理。扩容时首先将创建一个大小为原始数组的1.5倍。同时数组的大小不能超过Integer.MAX_VALUE。
public boolean add(E e) {
//首先检查范围容积和修改次数的统计,然后存储元素
    ensureCapacityInternal(size + 1);  // Increments modCount!!
    elementData[size++] = e;
    return true;
}
public void add(int index, E element) {
    rangeCheckForAdd(index);//检查index范围
    ensureCapacityInternal(size + 1);//保证容积足够,如果容积小了调用grow方法进行扩容
    System.arraycopy(elementData, index, elementData, index + 1,size - index);
    elementData[index] = element;
    size++;
}

remove()
调用remove(int index)时,首先会检查 index范围是否合法,然后再判断要删除的元素是否位于数组的末尾。如果index不是末尾位置,调用System.arraycopy()方法拷贝数组。将index + 1位置开始的元素之后所有的元素都向前移动一个位置。然后将数组的最后一个位置空,size - 1。如果index是最后一个元素那么就直接将数组的末尾置空,然后size - 1。当我们调用remove(Object o)时,会对o记性判空操作。然后对数组进行遍历,找到第一个与o对应的下标index,然后调用fastRemove()方法,删除下标为index的元素。

//remove(Object o)方法时先查找参数o的位置下标,然后调用fastRemove通过碎银济宁快速删除
public boolean remove(Object o) {
    if (o == null) {
        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;
}

//方法首先进行index的范围进行检查,然后elementData(index)获取指定数组位置index对应的元素,在使用size-index-1转换为numMoved移动元素个数,再使用Stem.arraycopy进行自我赋值,将最后一个位置的元素赋值置空
public E remove(int index) {
    rangeCheck(index);//检查index是否在范围内
    modCount++;//统计修改次数,用于实现遍历的同时修改数据的发现
    E oldValue = elementData(index);
    int numMoved = size - index - 1;//从数组中获取index位置上的元素
    if (numMoved > 0)
        System.arraycopy(elementData, index+1, elementData, index,numMoved);//拷贝从index+1之后的所有元素到目标数组elementData的index位置上
    elementData[--size] = null;//将最后一个位置的元素赋值为null
    return oldValue;
}

其他方法

//获取集合汇总的元素个数
public int size(){
	checkForComodification();
	return this.size;
}
//判断集合是否为空
public boolean isEmpty(){
	return size == 0;
}
//判断是否包含指定的元素
public boolean contains(Object o){
	return indexOf(o) >= 0;
}
//查找指定元素的第一个索引值
public int indexOf(Object o){

}

LinkedList

LinkedListd底层数据结构为双向链表,不受容器的限制,理论上可以五险增添元素,但是size限制了最大元素个数为Integer.MAX_VALUE。
成员属性

  • transient int size = 0; 元素个数
  • transient Node first;transient Node 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;
    }
}

构造方法

public LinkedList() {}
public LinkedList(Collection<? extends E> c) {
    this();
    addAll(c);
}
主要方法

add()

//头插法
private void linkFirst(E e) {
        final Node<E> f = first;//将头部的元素存储
        final Node<E> newNode = new Node<>(null, e, f);//创建新节点
        first = newNode;//新节点赋值给头节点
        if (f == null)//前驱节点为null
            last = newNode;//新节点就编程首地址
        else
            f.prev = newNode;//前驱节点不为null,前驱节点指向新节点
        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)//原来尾节点为null则直接将新节点赋值给尾结点
        first = newNode;
    else//如果不为null
        l.next = newNode;//将新节点添加在尾节点之后
    size++;
    modCount++;
}

remove()

//删除指定元素值,从头指针开始遍历整个链表,查找指定的Node节点
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;
}
//删除指定索引位置的元素
public E remove(int index) {
	checkElementIndex(index);//判断index是否在合法范围内
	return unlink(node(index));//node(index);是个查找方法,相当于整个遍历链表,这个方法先将索引和这个链表的size/2进行比对,若小于size/2则从头开始遍历到size/2位置。若大于size/2则从末尾开始向前遍历到size/2的位置进行查找。
}

get()

public E get(int index) {
	checkElementIndex(index);//判断index是否在合法范围内
	return node(index).item;
}

Vector

Vector的底层结构为数组,但是大部分方法上都有同步锁synchronized,所以该容器是一个安全的线程类。
成员属性

  • protected Object[] elementData; 用于存放数据的数组
  • protected int elementCount; 记录存放数据的个数
  • protected int capacityIncrement; 每次数组扩容的步长
    构造方法
//无参构造
public Vector() {
    this(10);//调用带参构造方法
}
public Vector(int initialCapacity) {
    this(initialCapacity, 0);//继续调用带两个参数的构造方法
}
public Vector(int initialCapacity, int capacityIncrement) {//参数1是初始化容积值,参数2是扩容步长值
    super();
    if (initialCapacity < 0)//初始化容积要合理
        throw new IllegalArgumentException("Illegal Capacity: "+initialCapacity);
    this.elementData = new Object[initialCapacity];//初始化数组
    this.capacityIncrement = capacityIncrement;
}
主要方法

add()

public synchronized boolean add(E e) {
    modCount++;
    ensureCapacityHelper(elementCount + 1);//当容积不足时会调用grow()方法,其扩容的倍数为原来的2倍
    elementData[elementCount++] = e;
    return true;
}
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) {
    if (minCapacity < 0)
        throw new OutOfMemoryError();
    return (minCapacity > MAX_ARRAY_SIZE) ? Integer.MAX_VALUE : MAX_ARRAY_SIZE;
}

总结

ArrayListLinkedListVector
实现方式底层结构为数组,按照索引编号访问速度快:O(1),但是删除或者添加元素时工作量大:O(n)底层结构为双向链表,按照索引编号访问速度慢:O(n),但是删除或者添加元素快:O(1)底层结构为数组,按照索引编号访问速度快:O(1),但是删除或者添加元素时工作量大:O(n)
是否同步线程不安全,但是线程的并发性高,访问效率高不同步,线程不安全,但是并发性高,访问效率高同步,线程安全,但是线程的并发性低,访问效率低
使用场景经常需要进行快速访问,很少添加或者删除元素的场景。如果需要多线程访问,则需要自行编程解决线程安全问题经常在集合中添加或者删除元素,并且很少通过索引对集合进行访问的场景。如果需要多线程访问,则需要自行编程解决线程安全问题一般不建议使用,如果在多线程并发访问并要求线程安全的场景使用
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值