学习Java中的List集合

网友投稿 311 2022-08-30


学习Java中的List集合

目录1.概述2.List的使用2.1List的常用方法3.List的实现类3.1ArrayList3.2Vector3.3LinkedList3.4ArrayList与Vector的区别

1.概述

List是一个有序集合(也被称为序列)。此接口的用户在列表中的每个元素都被插入的地方有精确的控制。用户可以通过它们的整数索引(在列表中的位置)访问元素,并在列表中搜索元素。 说是List集合,其实只是习惯说法,因为它是Collection接口的一个子接口(Collection有很多的子接口,这是其中三个主要的子接口之一,另外两个后面都会说到),所以Collection接口中定义的方法在List接口中也是可以使用的,另外还根据List的特点,又引入了其他的方法。

List接口的特点:

元素是以一种线性方式进行存储的元素存取有序,即元素的存入顺序和取出顺序一致。元素带有索引,通过索引就可以精确的操作集合中的元素(与数组类似)元素可以重复,通过元素的equals方法,来比较是否为重复的元素

2.List的使用

2.1List的常用方法

基本介绍:

这里说的常用方法是指除了实现Collection接口之外的。前面说到List集合中的元素是可以通过索引来操作集合中的元素的,所以List 集合里添加了一些根据索引来操作集合元素的方法。下面对这些方法进行简单

介绍:

void add(iint index, E element): 在列表中指定的位置上插入指定的元素boolean addAll(int index, Collection c): 将指定的集合中的所有元素插入到指定位置的列表中E get(int index):返回此列表中指定位置的元素List subList(int fromIndex, int toIndex):返回List中一部分对象的集合,即返回的集合是List的子集合,并是以下标索引取值。父集合List以fromIndex开始(包含),到toIndex结束(不包含)的 部分为返回的子集合int indexOf(Object obj):返回此列表中指定元素的第一个出现的索引,如果此列表不包含元素,返回- 1int lastIndexOf(Object obj):返回此列表中指定元素的最后一个发生的索引,如果此列表不包含元素,返回- 1E remove(int index):移除此列表中指定位置的元素E set(int index, E element):用指定元素替换此列表中指定位置的元素

代码示例:

public class ListDemo {

public static void main(String[] args) {

// 通过List的实现类ArrayList创建List集合对象

List list = new ArrayList();

// 指定位置添加元素

list.add(0,"jack");

list.add(1,"rose");

list.add(2,"marry");

System.out.println(list);

// 删除索引位置为2的元素

list.remove(2);

System.out.println(list);

// 指定元素替换此列表中指定位置的元素

list.set(0, "老王");

System.out.println(list);

// 获取指定位置元素(也遍历输出下)

for(int i = 0;i

System.out.println(list.get(i));

}

//还可以使用增强for

for (String string : list) {

System.out.println(string);

}

}

}

3.List的实现类

作为一个接口,List的实现类才是我们创建对象时候使用的(上面代码示例里面用到了ArrayList实现类)。在List接口里,有三个常用的实现类:ArrayList、Vector、LinkedList。下面从源码中分析和介绍它们。

3.1ArrayList

ArrayList底层通过数组实现,ArrayList可以随着元素的增加而动态扩容。它是一个数组队列,是java集合框架中使用最多的一个类,但是它是线程不安全的。

特点:以数组的形式进行存储,因此随机访问速度较快,所有它适用于查询缺点:不适用于插入和删除的操作 因为每次操作都需要移动数组中的元素;线程不安全

下面看下ArrayList的源码:

public class ArrayList extends AbstractList

implements List, RandomAccess, Cloneable, java.io.Serializable{

/**

* Default initial capacity. 初始化的时候如果没有指定长度的话,使用默认长度10

*/

private static final int DEFAULT_CAPACITY = 10;

/**

* Shared empty array instance used for empty instances. 空数组

*/

private static final Object[] EMPTY_ELEMENTDATA = {};

/**

* Shared empty array instance used for default sized empty instances. We

* distinguish this from EMPTY_ELEMENTDATA to know how much to inflate when

* first element is added. 空数组

*/

private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

/**

* Constructs an empty list with an initial capacity of ten.构造一个初始容量为10

*/

public ArrayList() {

this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;//初始化为空数组

}

public boolean add(E e) {

//查看当前数组是否够多存一个元素

ensureCapacityInternal(size + 1); // Increments modCount!!

//存入新元素到[size]位置,然后size自增1

elementData[size++] = e;

return true;

}

private void ensureCapacityInternal(int minCapacity) {

ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));

}

private static int calculateCapacity(Object[] elementData, int minCapacity) {

//如果当前数组还是空数组

if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {

//那么minCapacity取DEFAULT_CAPACITY与minCapacity的最大值

return Math.max(DEFAULT_CAPACITY, minCapacity);

}

//查看是否需要扩容

return minCapacity;

}

private void ensureExplicitCapacity(int minCapacity) {

modCount++;//修改次数加1

// 如果需要的最小容量比当前数组的长度大,即当前数组不够存,就扩容

if (minCapacity - elementData.length > 0)

grow(minCapacity);

}

private void grow(int minCapacity) {

// overflow-conscious code

int oldCapacity = elementData.length;//当前数组容量

int newCapacity = oldCapacity + (oldCapacity >> 1);//新数组容量是旧数组容量的1.5倍

//看旧数组的1.5倍是否够

if (newCapacity - minCapacity < 0)

newCapacity = minCapacity;

//看旧数组的1.5倍是否超过最大数组限制

if (newCapacity - MAX_ARRAY_SIZE > 0)

newCapacity = hugeCapacity(minCapacity);

//复制一个新数组

elementData = Arrays.copyOf(elementData, newCapacity);

}

public boolean remove(Object o) {

//先找到o在当前ArrayList的数组中的下标

//分o是否为空两种情况讨论

if (o == null) {

for (int index = 0; index < size; index++)

if (elementData[index] == null) {//null值用==比较

fastRemove(index);

return true;

}

} else {

for (int index = 0; index < size; index++)

if (o.equals(elementData[index])) {//非null值用equals比较

fastRemove(index);

return true;

}

}

return false;

}

private void fastRemove(int index) {

modCount++;//修改次数加1

//需要移动的元素个数

int numMoved = size - index - 1;

//如果需要移动元素,就用System.arraycopy移动元素

if (numMoved > 0)

System.arraycopy(elementData, index+1, elementData, index,

numMoved);

//将elementData[size-1]位置置空,让GC回收空间,元素个数减少

elementData[--size] = null; // clear to let GC do its work

}

public E remove(int index) {

rangeCheck(index);//检验index是否合法

modCount++;//修改次数加1

//取出[index]位置的元素,[index]位置的元素就是要被删除的元素,用于最后返回被删除的元素

E oldValue = elementData(index);

//需要移动的元素个数

int numMoved = size - index - 1;

//如果需要移动元素,就用System.arraycopy移动元素

if (numMoved > 0)

System.arraycopy(elementData, index+1, elementData, index,

numMoved);

//将elementData[size-1]位置置空,让GC回收空间,元素个数减少

elementData[--size] = null; // clear to let GC do its work

return oldValue;

}

public E set(int index, E element) {

rangeCheck(index);//检验index是否合法

//取出[index]位置的元素,[index]位置的元素就是要被替换的元素,用于最后返回被替换的元素

E oldValue = elementData(index);

//用element替换[index]位置的元素

elementData[index] = element;

return oldValue;

}

public E get(int index) {

rangeCheck(index);//检验index是否合法

return elementData(index);//返回[index]位置的元素

}

public int indexOf(Object o) {

//分为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;

}

public int lastIndexOf(Object o) {

//分为o是否为空两种情况

if (o == null) {

//从后往前找

for (int i = size-1; i >= 0; i--)

if (elementData[i]==null)

return i;

} else {

for (int i = size-1; i >= 0; i--)

if (o.equals(elementData[i]))

return i;

}

return -1;

}

从上面的源码中我们可以看到:

ArrayList 在初始化的时候如果我们没有指定长度的话,它会有一个默认长度10,每次扩容的时候为增加1.5倍然后是ArrayList 的一些常见的方法的源码介绍

3.2Vector

Vector的底层也是通过数组实现,方法与ArrayList基本一致,。但是Vector是线程安全的. 这是因为其加上了 synchronized 关键字, 用来保证线程安全。

优点: 以数组的形式进行存储,因此随机访问速度较快,所有它适用于查询;线程安全缺点: 不适用于插入和删除的操作 因为每次操作都需要移动数组中的元素

下面看下Vector的源码:

/**

* Constructs an empty vector so that its internal data array

* has size {@code 10} and its standard capacity increment is

* zero.

*/

public Vector() {

this(10); //指定初始容量initialCapacity为10

}

public Vector(int initialCapacity) {

this(initialCapacity, 0);//指定capacityIncrement增量为0

}

public Vector(int initialCapacity, int capacityIncrement增量为0) {

super();

//判断了形参初始容量initialCapacity的合法性

if (initialCapacity < 0)

throw new IllegalArgumentException("Illegal Capacity: "+

initialCapacity);

//创建了一个Object[]类型的数组

this.elementData = new Object[initialCapacity];//默认是10

//增量,默认是0,如果是0,后面就按照2倍增加,如果不是0,后面就按照你指定的增量进行增量

this.capacityIncrement = capacityIncrement;

}

//synchronized意味着线程安全的

public synchronized boolean add(E e) {

modCount++;

//看是否需要扩容

ensureCapacityHelper(elementCount + 1);

//把新的元素存入[elementCount],存入后,elementCount元素的个数增1

elementData[elementCount++] = e;

return true;

}

private void ensureCapacityHelper(int minCapacity) {

// overflow-conscious code

//看是否超过了当前数组的容量

if (minCapacity - elementData.length > 0)

grow(minCapacity);//扩容

}

private void grow(int minCapacity) {

// overflow-conscious code

int oldCapacity = elementData.length;//获取目前数组的长度

//如果capacityIncrement增量是0,新容量 = oldCapacity的2倍

//如果capacityIncrement增量是不是0,新容量 = oldCapacity + capacityIncrement增量;

int newCapacity = oldCapacity + ((capacityIncrement > 0) ?

capacityIncrement : oldCapacity);

//如果按照上面计算的新容量还不够,就按照你指定的需要的最小容量来扩容minCapacity

if (newCapacity - minCapacity < 0)

newCapacity = minCapacity;

//如果新容量超过了最大数组限制,那么单独处理

if (newCapacity - MAX_ARRAY_SIZE > 0)

newCapacity = hugeCapacity(minCapacity);

//把旧数组中的数据复制到新数组中,新数组的长度为newCapacity

elementData = Arrays.copyOf(elementData, newCapacity);

}

public boolean remove(Object o) {

return removeElement(o);

}

public synchronized boolean removeElement(Object obj) {

modCount++;

//查找obj在当前Vector中的下标

int i = indexOf(obj);

//如果i>=0,说明存在,删除[i]位置的元素

if (i >= 0) {

removeElementAt(i);

return true;

}

return false;

}

public int indexOf(Object o) {

return indexOf(o, 0);

}

public synchronized int indexOf(Object o, int index) {

if (o == null) {//要查找的元素是null值

for (int i = index ; i < elementCount ; i++)

if (elementData[i]==null)//如果是null值,用==null判断

return i;

} else {//要查找的元素是非null值

for (int i = index ; i < elementCount ; i++)

if (o.equals(elementData[i]))//如果是非null值,用equals判断

return i;

}

return -1;

}

public synchronized void removeElementAt(int index) {

modCount++;

//判断下标的合法性

if (index >= elementCount) {

throw new ArrayIndexOutOfBoundsException(index + " >= " +

elementCount);

}

else if (index < 0) {

throw new ArrayIndexOutOfBoundsException(index);

}

//j是要移动的元素的个数

int j = elementCount - index - 1;

//如果需要移动元素,就调用System.arraycopy进行移动

if (j > 0) {

//把index+1位置以及后面的元素往前移动

//index+1的位置的元素移动到index位置,依次类推

//一共移动j个

System.arraycopy(elementData, index + 1, elementData, index, j);

}

//元素的总个数减少

elementCount--;

//将elementData[elementCount]这个位置置空,用来添加新元素,位置的元素等着被GC回收

elementData[elementCount] = null; /* to let gc do its work */

}

从上面的源码中我们可以看到:

Vector在初始化的时候如果我们没有指定长度的话,它会有一个默认长度10,每次扩容的时候为增加2倍然后是Vector的一些常见的方法的源码介绍

3.3LinkedList

LinkedList底层的数据存储结构是链表结构,而且还是一个双向链表,可以实现双向操作。此外,LinkedList还实现了栈和队列的操作方法,因此也可以作为栈、队列和双端队列来使用,如peek 、push、pop等方法。

优点: 以链表形式进行存储,因此随机访问速度查询慢,增删快。缺点: 线程不安全

下面看一下源码:

int size = 0;

Node first;//记录第一个结点的位置

Node last;//记录最后一个结点的位置

private static class Node {

E item;//元素数据

Node next;//下一个结点

Node prev;//前一个结点

Node(Node prev, E element, Node next) {

this.item = element;

this.next = next;

this.prev = prev;

}

}

public boolean add(E e) {

linkLast(e);//默认把新元素链接到链表尾部

return true;

}

void linkLast(E e) {

final Node l = last;//用l 记录原来的最后一个结点

//创建新结点

final Node newNode = new Node<>(l, e, null);

//现在的新结点是最后一个结点了

last = newNode;

//如果l==null,说明原来的链表是空的

if (l == null)

//那么新结点同时也是第一个结点

first = newNode;

else

//否则把新结点链接到原来的最后一个结点的next中

l.next = newNode;

//元素个数增加

size++;

//修改次数增加

modCount++;

}

public boolean remove(Object o) {

//分o是否为空两种情况

if (o == null) {

pVrzFu//找到o对应的结点x

for (Node x = first; x != null; x = x.next) {

if (x.item == null) {

unlink(x);//删除x结点

return true;

}

}

} else {

//找到o对应的结点x

for (Node x = first; x != null; x = x.next) {

if (o.equals(x.item)) {

unlink(x);//删除x结点

return true;

}

}

}

return false;

}

E unlink(Node x) {//x是要被删除的结点

// assert x != null;

final E element = x.item;//被删除结点的数据

final Node next = x.next;//被删除结点的下一个结点

final Node prev = x.prev;//被删除结点的上一个结点

//如果被删除结点的前面没有结点,说明被删除结点是第一个结点

if (prev == null) {

//那么被删除结点的下一个结点变为第一个结点

first = next;

} else {//被删除结点不是第一个结点

//被删除结点的上一个结点的next指向被删除结点的下一个结点

prev.next = next;

//断开被删除结点与上一个结点的链接

x.prev = null;//使得GC回收

}

//如果被删除结点的后面没有结点,说明被删除结点是最后一个结点

if (next == null) {

//那么被删除结点的上一个结点变为最后一个结点

last = prev;

} else {//被删除结点不是最后一个结点

//被删除结点的下一个结点的prev执行被删除结点的上一个结点

next.prev = prev;

//断开被删除结点与下一个结点的连接

x.next = null;//使得GC回收

}

//把被删除结点的数据也置空,使得GC回收

x.item = null;

//元素个数减少

size--;

//修改次数增加

modCount++;

//返回被删除结点的数据

return element;

}

从上面的源码中我们可以看到:

LinkedList是基于链表的,所以没有扩容方法,默认加入元素是尾部自动扩容然后是LinkedList的一些常见的方法的源码介绍

3.4ArrayList与Vector的区别

它们的底层结构都是数组,我们称为动态数组。

ArrayList是新版的动态数组,线程不安全,效率高,Vector是旧版的动态数组,线程安全,效率低。动态数组的扩容机制不同,ArrayList扩容为原来的1.5倍,Vector扩容增加为原来的2倍。数组的初始化容量,如果在构建ArrayList与Vector的集合对象时,没有显式指定初始化容量,那么Vector的内部数组的初始容量默认为10,而ArrayList在JDK1.6及之前的版本也是10,而JDK1.7之后的版本ArrayList初始化为长度为0的空数组,之后在添加第一个元素时,再创建长度为10的数组。

System.out.println(list.get(i));

}

//还可以使用增强for

for (String string : list) {

System.out.println(string);

}

}

}

3.List的实现类

作为一个接口,List的实现类才是我们创建对象时候使用的(上面代码示例里面用到了ArrayList实现类)。在List接口里,有三个常用的实现类:ArrayList、Vector、LinkedList。下面从源码中分析和介绍它们。

3.1ArrayList

ArrayList底层通过数组实现,ArrayList可以随着元素的增加而动态扩容。它是一个数组队列,是java集合框架中使用最多的一个类,但是它是线程不安全的。

特点:以数组的形式进行存储,因此随机访问速度较快,所有它适用于查询缺点:不适用于插入和删除的操作 因为每次操作都需要移动数组中的元素;线程不安全

下面看下ArrayList的源码:

public class ArrayList extends AbstractList

implements List, RandomAccess, Cloneable, java.io.Serializable{

/**

* Default initial capacity. 初始化的时候如果没有指定长度的话,使用默认长度10

*/

private static final int DEFAULT_CAPACITY = 10;

/**

* Shared empty array instance used for empty instances. 空数组

*/

private static final Object[] EMPTY_ELEMENTDATA = {};

/**

* Shared empty array instance used for default sized empty instances. We

* distinguish this from EMPTY_ELEMENTDATA to know how much to inflate when

* first element is added. 空数组

*/

private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

/**

* Constructs an empty list with an initial capacity of ten.构造一个初始容量为10

*/

public ArrayList() {

this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;//初始化为空数组

}

public boolean add(E e) {

//查看当前数组是否够多存一个元素

ensureCapacityInternal(size + 1); // Increments modCount!!

//存入新元素到[size]位置,然后size自增1

elementData[size++] = e;

return true;

}

private void ensureCapacityInternal(int minCapacity) {

ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));

}

private static int calculateCapacity(Object[] elementData, int minCapacity) {

//如果当前数组还是空数组

if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {

//那么minCapacity取DEFAULT_CAPACITY与minCapacity的最大值

return Math.max(DEFAULT_CAPACITY, minCapacity);

}

//查看是否需要扩容

return minCapacity;

}

private void ensureExplicitCapacity(int minCapacity) {

modCount++;//修改次数加1

// 如果需要的最小容量比当前数组的长度大,即当前数组不够存,就扩容

if (minCapacity - elementData.length > 0)

grow(minCapacity);

}

private void grow(int minCapacity) {

// overflow-conscious code

int oldCapacity = elementData.length;//当前数组容量

int newCapacity = oldCapacity + (oldCapacity >> 1);//新数组容量是旧数组容量的1.5倍

//看旧数组的1.5倍是否够

if (newCapacity - minCapacity < 0)

newCapacity = minCapacity;

//看旧数组的1.5倍是否超过最大数组限制

if (newCapacity - MAX_ARRAY_SIZE > 0)

newCapacity = hugeCapacity(minCapacity);

//复制一个新数组

elementData = Arrays.copyOf(elementData, newCapacity);

}

public boolean remove(Object o) {

//先找到o在当前ArrayList的数组中的下标

//分o是否为空两种情况讨论

if (o == null) {

for (int index = 0; index < size; index++)

if (elementData[index] == null) {//null值用==比较

fastRemove(index);

return true;

}

} else {

for (int index = 0; index < size; index++)

if (o.equals(elementData[index])) {//非null值用equals比较

fastRemove(index);

return true;

}

}

return false;

}

private void fastRemove(int index) {

modCount++;//修改次数加1

//需要移动的元素个数

int numMoved = size - index - 1;

//如果需要移动元素,就用System.arraycopy移动元素

if (numMoved > 0)

System.arraycopy(elementData, index+1, elementData, index,

numMoved);

//将elementData[size-1]位置置空,让GC回收空间,元素个数减少

elementData[--size] = null; // clear to let GC do its work

}

public E remove(int index) {

rangeCheck(index);//检验index是否合法

modCount++;//修改次数加1

//取出[index]位置的元素,[index]位置的元素就是要被删除的元素,用于最后返回被删除的元素

E oldValue = elementData(index);

//需要移动的元素个数

int numMoved = size - index - 1;

//如果需要移动元素,就用System.arraycopy移动元素

if (numMoved > 0)

System.arraycopy(elementData, index+1, elementData, index,

numMoved);

//将elementData[size-1]位置置空,让GC回收空间,元素个数减少

elementData[--size] = null; // clear to let GC do its work

return oldValue;

}

public E set(int index, E element) {

rangeCheck(index);//检验index是否合法

//取出[index]位置的元素,[index]位置的元素就是要被替换的元素,用于最后返回被替换的元素

E oldValue = elementData(index);

//用element替换[index]位置的元素

elementData[index] = element;

return oldValue;

}

public E get(int index) {

rangeCheck(index);//检验index是否合法

return elementData(index);//返回[index]位置的元素

}

public int indexOf(Object o) {

//分为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;

}

public int lastIndexOf(Object o) {

//分为o是否为空两种情况

if (o == null) {

//从后往前找

for (int i = size-1; i >= 0; i--)

if (elementData[i]==null)

return i;

} else {

for (int i = size-1; i >= 0; i--)

if (o.equals(elementData[i]))

return i;

}

return -1;

}

从上面的源码中我们可以看到:

ArrayList 在初始化的时候如果我们没有指定长度的话,它会有一个默认长度10,每次扩容的时候为增加1.5倍然后是ArrayList 的一些常见的方法的源码介绍

3.2Vector

Vector的底层也是通过数组实现,方法与ArrayList基本一致,。但是Vector是线程安全的. 这是因为其加上了 synchronized 关键字, 用来保证线程安全。

优点: 以数组的形式进行存储,因此随机访问速度较快,所有它适用于查询;线程安全缺点: 不适用于插入和删除的操作 因为每次操作都需要移动数组中的元素

下面看下Vector的源码:

/**

* Constructs an empty vector so that its internal data array

* has size {@code 10} and its standard capacity increment is

* zero.

*/

public Vector() {

this(10); //指定初始容量initialCapacity为10

}

public Vector(int initialCapacity) {

this(initialCapacity, 0);//指定capacityIncrement增量为0

}

public Vector(int initialCapacity, int capacityIncrement增量为0) {

super();

//判断了形参初始容量initialCapacity的合法性

if (initialCapacity < 0)

throw new IllegalArgumentException("Illegal Capacity: "+

initialCapacity);

//创建了一个Object[]类型的数组

this.elementData = new Object[initialCapacity];//默认是10

//增量,默认是0,如果是0,后面就按照2倍增加,如果不是0,后面就按照你指定的增量进行增量

this.capacityIncrement = capacityIncrement;

}

//synchronized意味着线程安全的

public synchronized boolean add(E e) {

modCount++;

//看是否需要扩容

ensureCapacityHelper(elementCount + 1);

//把新的元素存入[elementCount],存入后,elementCount元素的个数增1

elementData[elementCount++] = e;

return true;

}

private void ensureCapacityHelper(int minCapacity) {

// overflow-conscious code

//看是否超过了当前数组的容量

if (minCapacity - elementData.length > 0)

grow(minCapacity);//扩容

}

private void grow(int minCapacity) {

// overflow-conscious code

int oldCapacity = elementData.length;//获取目前数组的长度

//如果capacityIncrement增量是0,新容量 = oldCapacity的2倍

//如果capacityIncrement增量是不是0,新容量 = oldCapacity + capacityIncrement增量;

int newCapacity = oldCapacity + ((capacityIncrement > 0) ?

capacityIncrement : oldCapacity);

//如果按照上面计算的新容量还不够,就按照你指定的需要的最小容量来扩容minCapacity

if (newCapacity - minCapacity < 0)

newCapacity = minCapacity;

//如果新容量超过了最大数组限制,那么单独处理

if (newCapacity - MAX_ARRAY_SIZE > 0)

newCapacity = hugeCapacity(minCapacity);

//把旧数组中的数据复制到新数组中,新数组的长度为newCapacity

elementData = Arrays.copyOf(elementData, newCapacity);

}

public boolean remove(Object o) {

return removeElement(o);

}

public synchronized boolean removeElement(Object obj) {

modCount++;

//查找obj在当前Vector中的下标

int i = indexOf(obj);

//如果i>=0,说明存在,删除[i]位置的元素

if (i >= 0) {

removeElementAt(i);

return true;

}

return false;

}

public int indexOf(Object o) {

return indexOf(o, 0);

}

public synchronized int indexOf(Object o, int index) {

if (o == null) {//要查找的元素是null值

for (int i = index ; i < elementCount ; i++)

if (elementData[i]==null)//如果是null值,用==null判断

return i;

} else {//要查找的元素是非null值

for (int i = index ; i < elementCount ; i++)

if (o.equals(elementData[i]))//如果是非null值,用equals判断

return i;

}

return -1;

}

public synchronized void removeElementAt(int index) {

modCount++;

//判断下标的合法性

if (index >= elementCount) {

throw new ArrayIndexOutOfBoundsException(index + " >= " +

elementCount);

}

else if (index < 0) {

throw new ArrayIndexOutOfBoundsException(index);

}

//j是要移动的元素的个数

int j = elementCount - index - 1;

//如果需要移动元素,就调用System.arraycopy进行移动

if (j > 0) {

//把index+1位置以及后面的元素往前移动

//index+1的位置的元素移动到index位置,依次类推

//一共移动j个

System.arraycopy(elementData, index + 1, elementData, index, j);

}

//元素的总个数减少

elementCount--;

//将elementData[elementCount]这个位置置空,用来添加新元素,位置的元素等着被GC回收

elementData[elementCount] = null; /* to let gc do its work */

}

从上面的源码中我们可以看到:

Vector在初始化的时候如果我们没有指定长度的话,它会有一个默认长度10,每次扩容的时候为增加2倍然后是Vector的一些常见的方法的源码介绍

3.3LinkedList

LinkedList底层的数据存储结构是链表结构,而且还是一个双向链表,可以实现双向操作。此外,LinkedList还实现了栈和队列的操作方法,因此也可以作为栈、队列和双端队列来使用,如peek 、push、pop等方法。

优点: 以链表形式进行存储,因此随机访问速度查询慢,增删快。缺点: 线程不安全

下面看一下源码:

int size = 0;

Node first;//记录第一个结点的位置

Node last;//记录最后一个结点的位置

private static class Node {

E item;//元素数据

Node next;//下一个结点

Node prev;//前一个结点

Node(Node prev, E element, Node next) {

this.item = element;

this.next = next;

this.prev = prev;

}

}

public boolean add(E e) {

linkLast(e);//默认把新元素链接到链表尾部

return true;

}

void linkLast(E e) {

final Node l = last;//用l 记录原来的最后一个结点

//创建新结点

final Node newNode = new Node<>(l, e, null);

//现在的新结点是最后一个结点了

last = newNode;

//如果l==null,说明原来的链表是空的

if (l == null)

//那么新结点同时也是第一个结点

first = newNode;

else

//否则把新结点链接到原来的最后一个结点的next中

l.next = newNode;

//元素个数增加

size++;

//修改次数增加

modCount++;

}

public boolean remove(Object o) {

//分o是否为空两种情况

if (o == null) {

pVrzFu//找到o对应的结点x

for (Node x = first; x != null; x = x.next) {

if (x.item == null) {

unlink(x);//删除x结点

return true;

}

}

} else {

//找到o对应的结点x

for (Node x = first; x != null; x = x.next) {

if (o.equals(x.item)) {

unlink(x);//删除x结点

return true;

}

}

}

return false;

}

E unlink(Node x) {//x是要被删除的结点

// assert x != null;

final E element = x.item;//被删除结点的数据

final Node next = x.next;//被删除结点的下一个结点

final Node prev = x.prev;//被删除结点的上一个结点

//如果被删除结点的前面没有结点,说明被删除结点是第一个结点

if (prev == null) {

//那么被删除结点的下一个结点变为第一个结点

first = next;

} else {//被删除结点不是第一个结点

//被删除结点的上一个结点的next指向被删除结点的下一个结点

prev.next = next;

//断开被删除结点与上一个结点的链接

x.prev = null;//使得GC回收

}

//如果被删除结点的后面没有结点,说明被删除结点是最后一个结点

if (next == null) {

//那么被删除结点的上一个结点变为最后一个结点

last = prev;

} else {//被删除结点不是最后一个结点

//被删除结点的下一个结点的prev执行被删除结点的上一个结点

next.prev = prev;

//断开被删除结点与下一个结点的连接

x.next = null;//使得GC回收

}

//把被删除结点的数据也置空,使得GC回收

x.item = null;

//元素个数减少

size--;

//修改次数增加

modCount++;

//返回被删除结点的数据

return element;

}

从上面的源码中我们可以看到:

LinkedList是基于链表的,所以没有扩容方法,默认加入元素是尾部自动扩容然后是LinkedList的一些常见的方法的源码介绍

3.4ArrayList与Vector的区别

它们的底层结构都是数组,我们称为动态数组。

ArrayList是新版的动态数组,线程不安全,效率高,Vector是旧版的动态数组,线程安全,效率低。动态数组的扩容机制不同,ArrayList扩容为原来的1.5倍,Vector扩容增加为原来的2倍。数组的初始化容量,如果在构建ArrayList与Vector的集合对象时,没有显式指定初始化容量,那么Vector的内部数组的初始容量默认为10,而ArrayList在JDK1.6及之前的版本也是10,而JDK1.7之后的版本ArrayList初始化为长度为0的空数组,之后在添加第一个元素时,再创建长度为10的数组。


版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:Python-函数(任意实参)
下一篇:eggNOG 5.0数据库介绍(eggnog怎么读音)
相关文章

 发表评论

暂时没有评论,来抢沙发吧~