List接口特点

1、有序的 collection。

2、可以对列表中每个元素的插入位置进行精确地控制。

3、可以根据元素的索引访问元素,并搜索列表中的元素。

4、列表通常允许重复的元素。

5、允许存在 null 元素。

6、实现List接口的常用类有LinkedList、ArrayList、Vector和Stack。

ArrayList特点

1、java.util.ArrayList<E> : List 接口的大小可变数组的实现类

2、ArrayList 内部基于 数组 存储 各个元素。

3、所谓大小可变数组,是指当 数组容量不足以存放新的元素时,创建新数组,并将原数组中的内容复制过来。

4、源码分析

//elementData中已存放的元素的个数,注意:不是elementData的容量
private int size;
//elementData的默认容量为10
private static final int DEFAULT_CAPACITY = 10;
//对象数组:ArrayList的底层数据结构,transient表示该字段不进行序列化操作
transient Object[] elementData;
//实例化一个空数组
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
//实例化一个空数组
private static final Object[] EMPTY_ELEMENTDATA = {}; protected transient int modCount = 0; @Native public static final int MAX_VALUE = 0x7fffffff; private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
/**
*指定初始容量的有参构造
*/
public ArrayList(int initialCapacity) {
//如果初始容量大于0就,对象数组就指向到一个新的数组,大小为所指定的大小
if (initialCapacity > 0) {
this.elementData = new Object[initialCapacity];
} else if (initialCapacity == 0) {
//如果等于0就指向一个空数组
this.elementData = EMPTY_ELEMENTDATA;
} else {
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
}
}
/**
* 无参构造
*/
public ArrayList() {
//对象数组就指向到一个空数组
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
/**
* 向elementData末尾中添加元素
*/
public boolean add(E e) {
//确保对象数组elementData有足够的容量,可以将新加入的元素e加进去
ensureCapacityInternal(size + 1);
//加入新元素e,size加1
elementData[size++] = e;
return true;
} // minCapacity = seize+1,即表示执行完添加操作后,数组中的元素个数
private void ensureCapacityInternal(int minCapacity) {
//判断是否是空数组
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
//用最小容量和10进行比较,取最大值赋值给最小容量
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
} ensureExplicitCapacity(minCapacity);
} /**
*确保数组的容量足够存放新加入的元素,若不够,要扩容
*/
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
//如果数组个数(size+1)数组长度就需要进行扩容
if (minCapacity - elementData.length > 0)
grow(minCapacity);
} private void grow(int minCapacity) {
int oldCapacity = elementData.length;
// 将旧的数组容量增加为原来的1.5倍作为新的容量
int newCapacity = oldCapacity + (oldCapacity >> 1);
//如果新的容量小于数组个数,将数组个数赋值给新容量
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) {
// 如果数组个数小于0抛出OutOfMemoryError异常
if (minCapacity < 0) // overflow
throw new OutOfMemoryError();
// 如果最数组个数大于最大容量 就返回最大值,否则返回最大容量
return (minCapacity > MAX_ARRAY_SIZE) ?
Integer.MAX_VALUE :
MAX_ARRAY_SIZE;
}
/**
* 向elementData指定位置添加元素
*/
public void add(int index, E element) {
//指定位置检查
rangeCheckForAdd(index);
// 扩容检查
ensureCapacityInternal(size + 1); // Increments modCount!!
//通过拷贝使数组内位置为 index 到 (size-1)的元素往后移动一位
System.arraycopy(elementData, index, elementData, index + 1,
size - index);
// 找到位置添加元素
elementData[index] = element;
// 元素个数加一
size++;
}
// 判断指定位置是否超出数组容量
private void rangeCheckForAdd(int index) {
if (index > size || index < 0)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
/**
* @param src 原数组.
* @param srcPos 原数组的起始位置.
* @param dest 目标数组.
* @param destPos 目标数组的起始位置.
* @param length 拷贝长度.
*/
public static native void arraycopy(Object src, int srcPos,
Object dest, int destPos,
int length); public E remove(int index) {
//指定位置检查
rangeCheck(index);
modCount++;
//保留要删除的值
E oldValue = elementData(index);
//要移动元素个数 int numMoved = size - index - 1;
if (numMoved > 0)
//通过拷贝使数组内位置为 index+1到 (size-1)的元素往前移动一位
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
//清除末尾元素让GC回收
elementData[--size] = null; // clear to let GC do its work
//返回删除的值
return oldValue;
}

ArrayList在无参的add方法中,每次插入新的元素时,判断数组是否为空,为空则建默认的容量10赋值给minCapacity,判断是否要扩容,第一次添加,数组的size是10,之后添加元素时,在ensureExplicitCapacity方法中判断数组元素个数即size+1(形参minCapacity)是否超过数组长度,超过则需要进行扩容操作,扩容是将旧的容量扩大到1.5倍,然后将数组拷贝到新的数组完成扩容操作。最后将元素添加,并size+1。ArrayList在指定位置添加元素时,是先检查指定位置是否在数组范围内,即数组中元素个数是1,则index得小于或者低于1。然后通过拷贝使数组内位置为 index 到 (size-1)的元素往后移动一位,腾出位置之后放入元素,数组个数加一。

Vector特点

1、java.util.Vector<E> :List 接口的内部用数组存放元素的实现类

2、内部也是用数组存放元素。

3、与 ArrayList 不同的是 扩容方式不同,Vector 每次增长的容量是固定的,大部分方法都被 synchronized 关键字修饰。

4、Vector 是线程安全的。

5、源码分析

protected transient int modCount = 0;
//elementData中已存放的元素的个数,注意:不是elementData的容量
protected int elementCount;
//对象数组:Vector的底层数据结构
protected Object[] elementData;
//增长系数
protected int capacityIncrement; @Native public static final int MAX_VALUE = 0x7fffffff; private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
/**
*指定初始容量和增长系数的有参构造
*/
public Vector(int initialCapacity, int capacityIncrement) {
super();
if (initialCapacity < 0)
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
//如果初始容量大于0就,对象数组就指向到一个新的数组,大小为所指定的大小
this.elementData = new Object[initialCapacity];
//给增长系数赋值
this.capacityIncrement = capacityIncrement;
}
/**
*无参构造
*/
public Vector() {
//调用指定初始容量的有参构造,设置默认初始容量为10 this(10);
}
/**
*指定初始容量的有参构造
*/
public Vector(int initialCapacity) {
//调用指定初始容量和增长系数的有参构造,设置增长系数为0
this(initialCapacity, 0);
} /**
* 向elementData末尾添加元素
*/
public synchronized boolean add(E e) {
modCount++;
//确保对象数组elementData有足够的容量,可以将新加入的元素e加进去
ensureCapacityHelper(elementCount + 1);
//加入新元素e,elementCount加1
elementData[elementCount++] = e;
return true;
}
public synchronized void addElement(E obj) {
modCount++;
//确保对象数组elementData有足够的容量,可以将新加入的元素e加进去
ensureCapacityHelper(elementCount + 1);
//加入新元素e,elementCount加1
elementData[elementCount++] = obj;
}
/**
* 向elementData指定位置添加元素
*/
public void add(int index, E element) {
insertElementAt(element, index);
} public synchronized void insertElementAt(E obj, int index) {
modCount++;
// 如果index大于数组个数抛出ArrayIndexOutOfBoundsException异常 if (index > elementCount) {
throw new ArrayIndexOutOfBoundsException(index
+ " > " + elementCount);
}
//确保对象数组elementData有足够的容量,可以将新加入的元素e加进去
ensureCapacityHelper(elementCount + 1);
System.arraycopy(elementData, index, elementData, index + 1, elementCount - index);
// 找到位置添加元素
elementData[index] = obj;
// 元素个数加一
elementCount++;
} /**
* @param src 原数组.
* @param srcPos 原数组的起始位置.
* @param dest 目标数组.
* @param destPos 目标数组的起始位置.
* @param length 拷贝长度.
*/
public static native void arraycopy(Object src, int srcPos,
Object dest, int destPos,
int length); 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) {
// 如果数组个数小于0抛出OutOfMemoryError异常
if (minCapacity < 0) // overflow
throw new OutOfMemoryError();
// 如果最数组个数大于最大容量 就返回最大值,否则返回最大容量
return (minCapacity > MAX_ARRAY_SIZE) ?
Integer.MAX_VALUE :
MAX_ARRAY_SIZE;
}

Vector和ArrayList一样都是基于数组实现的,且默认长度都是10,不同的是ArrayList扩容方式是扩大为原来的1.5倍,Vector默认是扩大为原来的两倍。其次Vector的方法都是同步的(Synchronized),是线程安全的,而ArrayList的方法不是。

LinkedList特点

1、java.util.LinkedList<E> :List 接口的实现类和Queue接口子类Deque的实现类

2、内部基于链表实现,增删快、迭代快,随机访问能力较差。

3、链表中的每个节点都是 LinkedList.Node 类型的对象。

4、LinkedList提供额外的get、remove、insert方法在LinkedList的首部或尾部。

5、这些操作使LinkedList可被用作堆栈(stack),队列(queue)或双向队列(deque)。

6、LinkedList没有同步方法。如果多个线程想访问同一个List,则必须自己实现访问同步。

7、源码分析

// 链表中存放节点个数
transient int size = 0;
// 链表头结点
transient Node<E> first;
// 链表尾结点
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;
}
}
/**
* 无参构造
*/
public LinkedList() {
}
/**
* 有参构造,将集合中元素插入链表
*/ public LinkedList(Collection<? extends E> c) {
this();
addAll(c);
} public boolean addAll(Collection<? extends E> c) {
//以size作为插入下标,插入集合中所有
return addAll(size, c);
} /**
* 以index为下标,添加所有到元素尾部
*/
public boolean addAll(int index, Collection<? extends E> c) {
//指定位置检查
checkPositionIndex(index);
//集合转换为数组
Object[] a = c.toArray();
//获取新增元素数量
int numNew = a.length;
//如果新增元素为0就不增加
if (numNew == 0)
return false;
Node<E> pred, succ;
//如果index是size,在链表尾部追加数据
if (index == size) {
succ = null;//原节点为空,
pred = last;//前置节点为尾节点,
} else {
//如果index!=size通过遍历获取到原节点
succ = node(index);
//原节点的前置节点通过pred进行保留
pred = succ.prev;
}
//遍历数组,依次插入节点
for (Object o : a) {
@SuppressWarnings("unchecked") E e = (E) o;
//生成新节点,前置节点为pred,后置节点为null
Node<E> newNode = new Node<>(pred, e, null);
//如果前置节点是null,则更新头节点
if (pred == null)
first = newNode; else
//如果前置节点不为null,则更新前置节点的后置节点为新节点
pred.next = newNode;
//设置当前节点为下次的前置节点,为下次插入做准备
pred = newNode;
}
//在链表尾部加数据时,原节点为null,则更新尾节点为最后插入的节点
if (succ == null) {
last = pred;
} else {
//不是在链表尾部追加数据时,将最后插入的节点的后置节点更新为原节点
pred.next = succ;
//原节点的前置节点更新为最后插入的节点
succ.prev = pred;
}
//更新节点个数
size += numNew;
modCount++;
return true;
}
/**
* 添加节点
*/
public boolean add(E e) {
//生成新节点 并插入到 链表尾部, 更新 last/first 节点。
linkLast(e);
return true;
}
//生成新节点 并插入到 链表尾部, 更新 last/first 节点。
void linkLast(E e) {
//用l记录尾节点
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加1
size++;
modCount++;
}
/**
* 添加节点到指定位置
*/
public void add(int index, E element) {
//指定位置检查
checkPositionIndex(index);
//指定位置等于元素个数,在尾节点插入
if (index == size)
linkLast(element);
else
//在中间插入
linkBefore(element, node(index));
} private void checkPositionIndex(int index) {
if (!isPositionIndex(index))
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
private boolean isPositionIndex(int index) {
return index >= 0 && index <= size;
} void linkBefore(E e, Node<E> succ) {
// 用pred记录指定位置节点(原节点)的前置节点
final Node<E> pred = succ.prev;
//生成新节点,前置节点为pred,后置节点为指定位置的节点(原节点)
final Node<E> newNode = new Node<>(pred, e, succ);
//将新节点作为原节点的前置节点
succ.prev = newNode;
//如果原节点的前置节点为空,更新头节点
if (pred == null)
first = newNode;
else
//如果原节点的前置节点不为空则更新前置节点的后置节点为新节点
pred.next = newNode;
//更新节点个数
size++;
modCount++;
}
/**
* 移除指定位置的节点
*/ public E remove(int index) {
//指定位置检查
checkElementIndex(index);
//移除节点
return unlink(node(index));
}
/**
* 移除节点
*/
E unlink(Node<E> x) {
// 记录节点信息
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;
//删除除节点的前置节点置为null
x.prev = null;
}
// 删除节点是否为尾节点
if (next == null) {
// 更新尾结点为删除节点的前置节点
last = prev;
} else {
//更新删除节点的后置节点的前置节点为删除节点的前置节点
next.prev = prev;
//删除除节点的后置节点置为null
x.next = null;
}
//删除除节点的元素值置为null
x.item = null;
//更新节点数并返回删除节点的元素值
size--;
modCount++;
return element;
}
/**
* 获取指定位置的节点的元素值
*/
public E get(int index) {
//指定位置检查
checkElementIndex(index);
//获取元素值
return node(index).item;
}
/**
*会根据index处于前半段还是后半段 进行一个折半,以提升查询效率
*/
Node<E> node(int index) {
//如果index< size/2从头结点开始遍历
if (index < (size >> 1)) {
Node<E> x = first;
for (int i = 0; i < index; i++)
x = x.next;
return x;
} else {
//如果index>=size/2从尾结点开始遍历
Node<E> x = last;
for (int i = size - 1; i > index; i--)
x = x.prev;
return x;
}
}

LinkedList的批量增加是找到添加的位置,然后通过循环数组,依次执行插入节点。LinkedList的移除节点则是先根据index判断及诶单是否存在,再根据所移除节点判断是否为头节点和尾节点,更新移除节点的前置节点和后置节点的信息,然后在把移除节点的信息置为null。查询操作则是根据index是处于前半段还是后半段,进行折半,以此来提高查询效率。

ArrayList和Vector

1、联系

  • 都是List接口(List接口继承了Collection接口)的实现类
  • 都是基于数组实现的,位置都是有序的,并且数据允许重复

2、区别

  • Vector类的方法大部分用synchronized修饰,是线程安全的,而ArrayList是线程不安全的,它的方法之前不是同步的
  • 扩容方式不同,Vector是默认增加容量一倍,或者是按照指定的容量增量进行增加。而ArrayList是增加容量为原来的二分之一

ArrayList和LinkedList

1、联系

  • 都是List接口(List接口继承了Collection接口)的实现类
  • 都是线程不安全的

2、区别

  • ArrayList是基于动态数组实现的,LinkedList是基于链表实现的
  • 对于随机访问get和set,ArrayList优于LinkedList,因为LinkedList是通过先折半然后遍历节点来获取指定节点,则ArrayList只需要通过索引就可以获取到
  • 对于新增和删除操作add和remove,LinkedList优于ArrayList,因为ArrayList要移动数据,而LinkedList只需要记录前后节点即可

牛客网笔试题

1、java 中的集合类包括 ArrayList 、 LinkedList 、 HashMap 等,下列关于集合类描述错误的是?

正确答案: C

A、ArrayList和LinkedList均实现了List接口

B、ArrayList访问速度比LinkedList快

C、随机添加和删除元素时,ArrayList的表现更加快速

D、HashMap实现Map接口,它允许任何类型的键和值对象,并允许将NULL用作键或值

2、实现或继承了Collection接口的是()

正确答案: B C E

A、Map

B、List

C、Vector

D、Iterator

E、Set

3、ArrayLists和LinkedList的区别,下述说法正确的有?

正确答案: A B C D

A、ArrayList是实现了基于动态数组的数据结构,LinkedList基于链表的数据结构。

B、对于随机访问get和set,ArrayList觉得优于LinkedList,因为LinkedList要移动指针。

C、对于新增和删除操作add和remove,LinkedList比较占优势,因为ArrayList要移动数据。

D、ArrayList的空间浪费主要体现在在list列表的结尾预留一定的容量空间,而LinkedList的空间花费则体现在它的每一个元素都需要消耗相当的空间。

参考博客链接

https://blog.csdn.net/zxt0601/article/details/77341098

转载请于明显处标明出处

https://www.cnblogs.com/AmyZheng/p/9428922.html

ArrayList、Vector和LinkedList的更多相关文章

  1. 常用Java API: ArrayList(Vector) 和 LinkedList

    摘要: 本文主要介绍ArrayList(Vector)和LinkedList的常用方法, 也就是动态数组和链表. ArrayList ArrayList 类可以实现可增长的对象数组. 构造方法 Arr ...

  2. ArrayList、Vector、LinkedList的区别联系?

    1.ArrayList.Vector.LinkedList类都是java.util包中,均为可伸缩数组. 2.ArrayList和Vector底层都是数组实现的,所以,索引数据快,删除.插入数据慢. ...

  3. ArrayList、Vector、LinkedList源码

    List接口的一些列实现中,最常用最重要的就是这三个:ArrayList.Vector.LinkedList.这里我就基于JDK1.7来看一下源码. public class ArrayList< ...

  4. ArrayList,Vector,LinkedList

    在java.util包中定义的类集框架其核心的组成接口有如下:·Collection接口:负责保存单值的最大父接口 |-List子接口:允许保存重复元素,数据的保存顺序就是数据的增加顺序: |-Set ...

  5. ArrayList Vector LinkedList 区别与用法

    转载自: http://www.cnblogs.com/mgod/archive/2007/08/05/844011.html 最近用到了,所以依然是转载 ArrayList 和Vector是采用数组 ...

  6. Arraylist Vector Linkedlist区别和用法 (转)

    ArrayList 和Vector是采用数组方式存储数据,此数组元素数大于实际存储的数据以便增加和插入元素,都允许直接序号索引元素,但是插入数据要设计到数组元素移动等内存操作,所以索引数据快插入数据慢 ...

  7. paip.提升性能---list,arraylist,vector,linkedlist,map的选用..

    paip.提升性能---list,arraylist,vector,linkedlist,map的选用.. arraylist,vector基本一样,但是,vector线程安全的. 作者Attilax ...

  8. ArrayList、Vector、LinkedList的区别及其优缺点? (转载)

    原文链接:http://blog.csdn.net/wangzff/article/details/7296648 ArrayList,LinkedList,Vestor这三个类都实现了java.ut ...

  9. List接口实现类-ArrayList、Vector、LinkedList集合深入学习以及源代码解析

    学习List接口实现类 ArrayList  Vector  LinkedList List接口的实现类中最经常使用最重要的就是这三个:ArrayList.Vector.LinkedList. JDK ...

  10. Java ArrayList、Vector和LinkedList等的差别与用法(转)

    Java ArrayList.Vector和LinkedList等的差别与用法(转) ArrayList 和Vector是采取数组体式格式存储数据,此数组元素数大于实际存储的数据以便增长和插入元素,都 ...

随机推荐

  1. 题解【CodeForces1154A】Restoring Three Numbers

    Description Polycarp has guessed three positive integers \(a\), \(b\) and \(c\). He keeps these numb ...

  2. 线性回归-Fork

    线性回归 主要内容包括: 线性回归的基本要素 线性回归模型从零开始的实现 线性回归模型使用pytorch的简洁实现   线性回归的基本要素 模型 为了简单起见,这里我们假设价格只取决于房屋状况的两个因 ...

  3. 牛客多校第一场 A Equivalent Prefixes 单调栈(笛卡尔树)

    Equivalent Prefixes 单调栈(笛卡尔树) 题意: 给出两个数组u,v,每个数组都有n个不同的元素,RMQ(u,l,r)表示u数组中[l,r]区间里面的最小值标号是多少,求一个最大的m ...

  4. 每天进步一点点------Allegro PCB

    Allegro PCB 1.如何在allegro中取消花焊盘(十字焊盘) set up->design parameter ->shape->edit global dynamic ...

  5. Codeforces Global Round 3:B. Born This Way

    Born This Way原文链接:[传送门] 题目大意:潇洒哥想乘坐飞机从A地到达C地,但是没有直达的航班,在A地和B地之间有一个可以中转的航班B,潇洒哥想早点到达C地(有航班就坐),但是很不幸他得 ...

  6. PyQt5遇到的一个坑 "ImportError: unable to find Qt5Core.dll on PATH" 及解决办法

    最近再实现一个功能,主要是将自动化测试界面化 环境组合为:Windows 64bit + PyCharm + Python + PyQt5 + Pyinstaller + Inno Setup PS ...

  7. RestTemplate-记录

    org.springframework.web.client.RestTemplate 1.从使用功能上看,是一种简化请求响应的工具类,从发送请求,到对返回的结果进行json解析.格式不对会有异常.

  8. io型和有状态的应用不放入k8s,而是做服务映射

    io型和有状态的应用不放入k8s,而是做服务映射 待办 在实际应用中,一般不会把mysql这种重IO.有状态的应用直接放入k8s中,而是使用专用的服务器来独立部署.而像web这种无状态应用依然会运行在 ...

  9. 数据库备份与还原:mysqldump,source

    *数据库备份* 1.备份方法一:适用于myslam表: 直接将tb_name.frm.tb_name.myd.tb_name.myi三个文件保存,备份即可. 需要的时候直接解压到,移动到相应的数据库目 ...

  10. 时间类型:datetime,timestamp,date,time,year

    时间类型 1.年月日时分秒:datetime #取值范围# '1000-01-01 00:00:00'到'9999-12-31 23:59:59' 占存储8B:表示的范围比timestamp大:支持0 ...