List概述

List是一个有序,可重复的集合,可以在List的中间插入和移除元素,根据整数索引访问元素

下图是List集合的框架图

gui java 列表显示 java的列表_指定位置

下面是对上图的简单介绍

AbstractCollection: 提供 Collection 接口的骨干实现

Iterator: 迭代器

ListIterator:列表迭代器

Queue:队列

Deque:一个线性 collection,支持在两端插入和移除元素

AbstractSequentialList:提供了 List 接口的骨干实现

LinkedList:链表的实现

ArrayList:大小可变数组的实现

Vector:实现可增长的对象数组

Stack:后进先出(LIFO)的对象堆栈

ArrayList

底层存储

属性 elementData 存储集合中的内容

/**缓存区:存储元素*/

private transient Object[] elementData;

将元素添加到指定位置

将元素添加到指定位置后, 把原数组中 该位置和之后的元素 向后移动一位

/**在指定位置添加元素*/
public void add(intindex, E element) {
rangeCheckForAdd(index);//判断索引位置是否正确
ensureCapacityInternal(size+ 1); //扩容//对原数组进行复制处理(位移),从index + 1到size-index//即向右移动当前位于该位置的元素以及所有后续元素。
System.arraycopy(elementData, index, elementData, index + 1,
size-index);
elementData[index]= element; //插入
size++;
}

介绍下 System.arraycopy(......)

/**即从指定源数组中复制一个数组,

复制从指定的位置开始,到目标数组的指定位置结束。

将原数组src从srcPos位置开始复制到dest数组中,到dest的destPos位置开始,复制的长度为length*/

public static native void arraycopy(Object src, int srcPos, Object dest, int destPos, int length);

删除指定位置的元素

删除元素后, 把原数组中 该位置之后的元素 向前移动一位

public E remove(intindex) {
rangeCheck(index);//判断移动位置
modCount++; //记录改变次数
E oldValue = elementData(index); //要删除的元素
int numMoved = size - index - 1; //移动的长度//向左移动numMoved个长度
if (numMoved > 0)
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
elementData[--size] = null; //把最后一个元素设为null
returnoldValue;
}

扩容

首先扩容为原来的1.5倍, 在检查新容量,  最大容量为Integer.MAX_VALUE

private void grow(intminCapacity) {

...int newCapacity = oldCapacity + (oldCapacity >> 1); //将新的容量变为原来容量的1.5倍。

...

hugeCapacity(intminCapacity);

}//对扩容后的容量进行检查

private static int hugeCapacity(intminCapacity) {if (minCapacity < 0) //得到的容量<0,为什么会有这种情况,内存溢出了。

throw newOutOfMemoryError();return (minCapacity > MAX_ARRAY_SIZE) ? //如果需要的容量比规定的最大容量大,那么最大容量只能是 Integer.MAX_VALUE。

Integer.MAX_VALUE :

MAX_ARRAY_SIZE;

}

动态扩容,其实是一个新数组; 在清楚业务,插入的数据量大于扩容后的1.5倍, 应该自己设置一个合适容量

Vector 和 Stack

概述

Vector可以看做ArrayList的同步实现, 具体可参考ArrayList的分析

Stack类提供了栈的结构, 表示先进后出的操作方法

Stack 继承 Vector , 它定义了5个方法并且调用的都是父类中的方法, 下面是方法定义

E push(E item) //把对象压入栈顶部, 内部调用是父类的 addElement(item)

E pop() //移除栈顶对象,内部调用是父类的 removeElementAt(int index)

E peek() //查看栈顶的对象,内部调用是父类的 elementAt(int index)

int search(Object o) //返回对象在堆栈中的位置,内部调用父类lastIndexOf(Object o)

empty() //测试堆栈是否为空

自定义Stack

/*** 自定义Stack*/
public classMyStack {private static final int CAPACITY = 3;private static Object[] array = newObject[CAPACITY];private static int top = -1;/**把对象压入栈顶*/
void push(Object o) throwsException {if(getSize() ==CAPACITY) {throw new ExceptionStack("stack is full");
}
array[++top] =o;
}/**移除栈顶元素*/Object pop()throwsException {if(isEmpty()) {throw new ExceptionStack("stack is empty");
}return array[top--];
}/**查看栈顶元素*/Object peek()throwsException {if(isEmpty()){throw new ExceptionStack("Stack is empty");
}returnarray[top];
}/**判断尺寸已满*/
intgetSize() {if(isEmpty()){return 0;
}else{return top + 1;
}
}/**判断为空*/
booleanisEmpty() {return (top < 0);
}public static void main(String[] args) throwsException {
MyStack s= newMyStack();
System.out.println(s.isEmpty());
s.push("1");
s.push("2");
s.push("3");
System.out.println(s.isEmpty());
System.out.println(s.getSize());
System.out.println(s.pop());
System.out.println(s.peek());
}
}

自定义异常类:

public class ExceptionStack extendsException{//Define myself exception construct with parameters
publicExceptionStack(String string){super(string);
}
}

LinkedList

LinkedList 的内部实现是双向链表,它允许null的存在,它的方法是不同步的

与ArrayList相比,在插入和删除时优于ArrayLis, 而随机访问则比ArrayList逊色些

主要属性

transient int size = 0; //集合长度

transient Node first; //头节点

transient Node last; //尾节点

介绍下静态内部类Node

private static class Node{

E item;//原节点

Node next; //指向后一个节点

Node prev; //指向前一个节点//构造方法

Node(Node prev, E element, Nodenext) {this.item =element;this.next =next;this.prev =prev;

}

}

构造方法

分析下:  LinkedList(Collection extends E> c)  构造一个包含指定 collection 中的元素的列表

最终调用的是  addAll(..) ,如下:

1 public boolean addAll(int index, Collection extends E>c) {2 checkPositionIndex(index); //判断下表越界
3
4 Object[] a = c.toArray(); //把c转为数组
5 int numNew = a.length; //numNew为数组长度
6 if (numNew == 0)7 return false;8
9 Nodepred, succ;10
11 if (index == size) { //在构造的调用过程中,肯定是相等的
12 succ = null;13 pred = last; //pred指向尾节点
14 }15
16 /* 注释的是构造时不执行的
17 else {18 succ = node(index);19 pred = succ.prev;20 }21 */
22
23 for(Object o : a) {24 E e = (E) o; //从数组中取出元素
25 Node newNode = new Node<>(pred, e, null); //创建新节点
26
27 /*
28 if (pred == null)29 first = newNode;30 */
31 else
32 pred.next = newNode; //原尾节点的后一个节点指向新节点
33 pred = newNode; //pred成了新节点
34 }35
36 if (succ == null) {37 last = pred; //last是添加c后的尾节点
38 }39
40 /*
41 else {42 pred.next = succ;43 succ.prev = pred;44 }45 */
46
47 size += numNew; //修改容量
48 modCount++; //修改次数+1
49 return true;50 }
node(int index) 返回指定位置的节点
node(int index) 很重要, 添加,删除等操作都会用到, 下面就介绍它
/**返回指定位置的节点*/Node node(intindex) {//判断遍历的方向
if (index < (size >> 1)) { //size >> 1 = siz2 /2
Node x = first; //从指定位置的节点开始往前遍历
for (int i = 0; i < index; i++)
x=x.next;returnx;
}else{
Node x = last; //从尾节点开始向指定位置遍历
for (int i = size - 1; i > index; i--)
x=x.prev;returnx;
}
}
添加
add(E e ): 把节点加到链表的最后 ,实际上调用的是linkLast(E e)方法
voidlinkLast(E e) {final Node l = last; //把末尾节点保存
final Node newNode = new Node<>(l, e, null); //创建一个新节点,并指定前一个节点是原链表的末尾节点
last = newNode; //最后一个节点的引用指向新节点
if (l == null)
first=newNode;elsel.next= newNode; //原链表的末尾节点的下一个节点指向新节点
size++; //链表总数+1
modCount++;
}

add(int index, E element): 把数据插入指定的位置,实际上调用的是 linkBefore(element, node(index))

/**参数1: 代表新元素

参数2: succ 代表指定位置的节点*/

void linkBefore(E e, Nodesucc) {final Node pred = succ.prev; //把指定位置的前一个节点保存在pred
final Node newNode = new Node<>(pred, e, succ); //创建新节点
succ.prev = newNode; //指定新节点的后一个节点
if (pred == null)
first=newNode;elsepred.next= newNode; //指定新节点的前一个节点
size++;
modCount++;
}

删除

remove(Object o):  从此列表中移除首次出现的指定元素, 实际上调用的是 unlink(Node x)

/**参数: 要移除的节点*/E unlink(Nodex) {final E element =x.item;final Node next = x.next; //保存 移除节点 的后一个节点 在变量next中

final Node prev = x.prev; //保存 移除节点 的前一个节点 在变量prev中
if (prev == null) { //true代表 移除节点 是头节点
first =next;
}else{
prev.next= next; //将 prev后一个节点 指向next
x.prev = null; //将 移除节点的prev设为空
}if (next == null) { //true代表移除节点是尾节点
last =prev;
}else{
next.prev= prev; //将 next前一个节点 指向prev
x.next = null; //将 移除节点的next 设为空
}
x.item= null; //将移除节点的元素 设为空
size--;
modCount++;return element; //返回移除的元素
}

总结

对LinkedList的操作实际上是对指向前节点和后节点的引用操作,所以其插入和删除效率较高,但是随机访问效率较差,因为要遍历