1、List接口
List是有序可重复的集合(也称为列表)。它在Collection接口的基础上新增了一些与索引相关的方法,其中包括:
- add(int index, E element):在索引处添加元素;
- addAll(int index, Collection<? extends E> c):在索引处添加集合;
- get(int index):获取索引处的元素;
- indexOf(Object o):返回元素第一次出现在列表中的索引位置;
- lastIndexOf(Object o):返回最后一次出现在列表中的索引位置;
- remove(int index):删除指定索引中的元素;
- set(int index, E element):用新元素代替指定索引处的元素;
- subList(int fromIndex, int toIndex)返回指定索引范围中的元素形成新的列表;
- listIterator(int index)或listIterator():返回列表迭代器;
List接口的实现类有ArrayList和LinkedList;在JDK8中,ArrayList除了实现List接口外,还实现了RandomAccess、Cloneable、Serializable,以及继承AbstractList抽象类;
而LinkedList实现了Deque, Cloneable, Serializable,以及继承AbstractSequentialList;
2、ArrayList
2.1、ArrayList特征
(1) 有序集合(输出顺序==输入顺序),因为底层是通过数组来存储元素;
/*
采用transient修饰成员变量,使得默认对象序列化时对elementData存储失效,之所以如此定义,
是因为elementData数组并不是装满了元素,真正有元素的大小为size,
所以可通过覆盖readObject和writeObject方法自定义对elementData的序列化
*/
transient Object[] elementData;
(2) 动态扩容,最大可容Integer.MAX_VALUE个元素,在通过new ArrayList()创建列表时,默认初始容量大小为DEFAULT_CAPACITY=10;
private void ensureExplicitCapacity(int minCapacity) {
//修改次数加1
modCount++;
//elemntData数组长度小于minCapacity时发生扩容
if (minCapacity - elementData.length > 0)
grow(minCapacity);//核心动态扩容机制
}
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
//newCapacity为1.5倍的oldCapacity
int newCapacity = oldCapacity + (oldCapacity >> 1);
//与最小容量值minCapacity比较
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)
//限制最大可容元素大小为Integer.MAX_VALUE
newCapacity = hugeCapacity(minCapacity);
//动态扩容
elementData = Arrays.copyOf(elementData, newCapacity);
}
(3) 线程不安全,因为在实现和继承的方法中不包含lock和synchronized字眼;
(4) 增删改查速度快;
前面已经说明ArrayList底层采用数组来存储数据,那么我们自认为增删数据速度应该比LinkedList慢很多,但是从源码中可以看到增删方法采用JVM固有的System. arrayCopy()方法进行了效率优化,所以当元素量大的时候,其速度会比用For循环快些;
2.2ArrayList简单用法
(LinkedList一样可以用)
package xw.zx.collection;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
import java.util.ListIterator;
public class ListDemo {
public static void main(String[] args) {
// TODO Auto-generated method stub
List<String> l=new ArrayList<String>();
//添加元素
l.add("1");//索引从0开始
l.add(1,"2");//在索引1处添加字符串2
l.addAll(Arrays.asList("3","4","5"));//在size后添加集合内的元素
/**
* 在索引处添加集合中的元素,当index<size的时候,[index,size]范围内的数据将被移到最后面
* 本例中元素5被移到了列表的末尾;
*/
l.addAll(4,Arrays.asList("6","7","8"));
System.out.println(l);//[1, 2, 3, 4, 6, 7, 8, 5]
//删除元素
l.remove(2);//删除索引处的元素
l.remove("5");//删除元素"5";
/**
* 删除参数集合中包含的元素
* 参数集合中可包含l列表中未存在的元素,比如“9”
*/
l.removeAll(Arrays.asList("4","7","9"));
System.out.println(l);//[1, 2, 6, 8]
//修改元素
l.set(1,"6");//将索引1中的元素替换成元素“6”,可见列表元素可重复
System.out.println(l);//[1, 6, 6, 8]
//返回子列表,包括fromIndex,不包括toIndex
List<String> sub=l.subList(1, 3);
System.out.println(sub);//[6,6]
//将列表转化为数组
String[] arr=l.toArray(new String[2] );
System.out.println(Arrays.deepToString(arr));//[1,6,6,8]
//支持双向遍历
ListIterator<String> listIterator=l.listIterator(l.size());
while(listIterator.hasPrevious()) {
//向后遍历
System.out.print(listIterator.previous()+" ");
}//8 6 6 1
System.out.println();
while(listIterator.hasNext()) {
//向前遍历
System.out.print(listIterator.next()+" ");
}//1 6 6 8
}
}
3、LinkedList
3.1LinkedList特征
(1) 采用双向链式结构存储数据,使得元素的增、删速度快;
//链首节点
transient Node<E> first;
//链尾节点
transient Node<E> last;
(2) 实现了Deque接口方法,除了List接口方法可使用外,还可用于实现队列Queue(FIFO)、栈Stack(LIFO)结构特征;下面以实现队列为例
package xw.zx.collection;
import java.util.LinkedList;
public class TestQueue<E> {
private LinkedList<E> list = new LinkedList<E>();
/**
* 清空队列
*/
public void clear()
{
list.clear();
}
/**
* 判断队列是否为空
* @return
*/
public boolean isEmpty()
{
return list.isEmpty();
}
/**
* 在队尾中插入元素
* @param e
*/
public void push(E e)
{
list.addLast(e);
}
/**
* 从队首中弹出元素
* @return
*/
public E poll()
{
if(!list.isEmpty())
{
return list.removeFirst();
}
return null;
}
/**
* 队列元素的大小
* @return
*/
public int size()
{
return list.size();
}
/**
* 返回队首的元素
* @return
*/
public E peek()
{
return list.getFirst();
}
/**
* 测试
* @param args
*/
public static void main(String[] args)
{
TestQueue<String> queue = new TestQueue<String>();
queue.push("1");//推进元素
queue.push("2");
queue.push("3");
System.out.println("判断是否队列是否为空:"+queue.isEmpty());
System.out.println("返回队列大小:"+queue.size());
System.out.println("弹出队首元素,并删除队首元素:"+queue.poll());
System.out.println("获取队首元素:"+queue.peek());
queue.clear();
System.out.println("判断是否队列是否为空:"+queue.isEmpty());
}
}
(3) 查寻速度慢,由于采用双向链式表存储数据,所以每次查寻元素都需要从链头或链尾挨个节点遍历查找;
//JDK8源码LinkedList中的get方法
public E get(int index) {
//检查index有没有超出size大小
checkElementIndex(index);
//调用node方法返回元素值
return node(index).item;
}
//JDK8源码LinkedList中的node方法
Node<E> node(int index) {
//判断index是靠近链前还是链后,然后在决定遍历方式
if (index < (size >> 1)) {
Node<E> x = first;
for (int i = 0; i < index; i++)
x = x.next;
return x;
} else {
Node<E> x = last;
for (int i = size - 1; i > index; i--)
x = x.prev;
return x;
}
}
4、总结
(1)List的所有实现类都是有序可重复的,ArrayList底层是使用数组存储,LinkedList采用双向链表;
(2)ArrayList比LinkedList更常用一些,因为它的增删改查速度都快(增删采用System.arrayCopy()优化),而LinkedList的查寻速度比较慢,需要从链头或链尾挨个遍历查寻;
(3)LinkedList适用于构造队列Queue、栈Stack数据结构;