三大数据结构链表、树和图,顺序表作为当中的一种,能够说是平时编程中最长使用到的。
List接口是顺序表在java中的实现。它有非常多子接口和实现类,平时的编程中使用起来非常方便。可是更进一步,我们有必要对事实上现和原理进行理解,并和数据结构中所学比較,并应用于平时的编程中,编写出高效率的代码。
首先看下list接口的层次关系,下图由本人依据jdk的类结构简单画的:
从上图能够看出,list接口有Collection接口衍生而出。那么首先了解下Collection接口。
Collection
collection 是集合框架的根。他定义的集合操作的通用行为,如加入元素、批量加入、删除、批量删除、集合大小、包括、迭代器等。它的接口定义这里不再贴。在关系上,Collection是继承于Iterable接口的,其仅仅有一个方法:
public interface Iterable<T> {
/**
* Returns an {@link Iterator} for the elements in this object.
*
* @return An {@code Iterator} instance.
*/
Iterator<T> iterator();
}
当中Iterator例如以下:
public interface Iterator<E> {
public boolean hasNext();
public E next();
public void remove();
}
AbstractCollection
AbstractCollection 是Collection接口的抽象实现,实现了当中大部分的功能。例如以下所看到的,我们实现自己的Collection仅仅须要实现三个方法就可以:
Collection<String> c =
/**
*
*自己定义实现演示样例,在AbstractCollection 中的一些方法的实现中,如Clear。调用了
*iterator()方法,而在这些方法在子类中,实现这是典型的template模式。
*
*/
new AbstractCollection<String>() {
/* *
* 迭代器
*/
@Override
public Iterator<String> iterator() {
// TODO Auto-generated method stub
return null;
}
/*
* 大小
*/
@Override
public int size() {
// TODO Auto-generated method stub
return 0;
}
/*
* 抽象实现是抛出异常。我们须要自己实现
*/
@Override
public boolean add(String e) {
return true;
}
};
例如以下代码片段摘自AbstractCollection,调用了iterator方法,当中有非常多类似代码,如addAll、removeAll、contains、toArray()等,这些实现仅仅是主要的实现,子类中有更有效率的就会覆盖它。典型的可见后面的Arraylist的toArray()。
public void clear() {
Iterator<E> e = iterator();
while (e.hasNext()) {
e.next();
e.remove();
}
}
List
List接口表示数据结构中的链表,其继承collection接口。又往里面加入了一些链表操作的方法,主要是随机訪问、删除、查找、专用的迭代器等,例如以下所看到的:
/**
随机获取
*/
E get(int index);
/**
* 随机设置值
*/
E set(int index, E element);
/**
* 随机加入
*/
void add(int index, E element);
/**
*随机移除
*/
E remove(int index);
// Search Operations
/**
* 查找
*/
int indexOf(Object o);
/**
* 从后查找
*/
int lastIndexOf(Object o);
// List Iterators
/**
*专用迭代
*/
ListIterator<E> listIterator();
/**
* 从某个位置迭代
*/
ListIterator<E> listIterator(int index);
// View
/**
* 子列表
*/
List<E> subList(int fromIndex, int toIndex);
AbstractList
这是List接口的抽象实现,和AbstractCollection类似,实现了主要的功能而把关键的方法延迟到子类中实现。例如以下所看到的一个示意子类:
List<String> l = /**
*
*演示样例实现
*/
new AbstractList<String>() {
/*
* 随机获取
*/
@Override
public String get(int index) {
return null;
}
/*
* 大小
*/
@Override
public int size() {
return 0;
}
/*
* 超类中实现抛出异常。表示不可变list
*
* 自己实现后变为可变
*/
@Override
public String set(int index, String element) {
return null;
}
/*
* 默认实现抛出异常
*/
@Override
public void add(int index, String element) {
}
/**
* 默认实现抛出异常
*/
@Override
public String remove(int index) {
return null;
}
};
ListIterator
List接口加入了新的方法,当中一个方法就是返回ListIterator,其扩展了iterator。添加了向前遍历的方法。
主要链表是有序的。
事实上现不多说。须要注意的就是迭代器失效问题,在list实现中。维护了一个字段modCount,当此list进行改变操作,如add/remove等的时候,此值会进行改变。当构造迭代器的时候,此值会在迭代器中保留一个副本,使用迭代器中的方法都会检查副本和list中的modCount是否一致,假设不一致,抛出迭代器异常。
须要注意的是,java中的for each语法。经过编译后,是使用的迭代器。
例如以下部分源代码演示样例:
private class Itr implements Iterator<E> {
/** 此类是个内部类。直接訪问abstractList的字段
* Index of element to be returned by subsequent call to next.
*/
int cursor = 0;
/**
* Index of element returned by most recent call to next or
* previous. Reset to -1 if this element is deleted by a call
* to remove.
*/
int lastRet = -1;
/**
* 注意这点
*/
int expectedModCount = modCount;
/**
*迭代器的next方法
*/
public E next() {
/**
*操作前进行检查
*/
checkForComodification();
try {
E next = get(cursor);
lastRet = cursor++;
return next;
} catch (IndexOutOfBoundsException e) {
checkForComodification();
throw new NoSuchElementException();
}
}
/**
*检查迭代器失效问题。
*/
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
}
ArrayList
这是一般编程中最长使用的数据结构,基于数组实现的顺序表。可是数组可自己主动扩容。它直接继承于abstractList,故仅仅研究其关键实现和方法。
数组存储
ArrayList是基于数组的存储。默认构造初始大小是10,后面我们会看到这个初始大小会一定程度上影响其性能:
/**
* The array buffer into which the elements of the ArrayList are stored.
* The capacity of the ArrayList is the length of this array buffer.
*/
private transient Object[] elementData;
public ArrayList(int initialCapacity) {
super();
if (initialCapacity < 0)
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
this.elementData = new Object[initialCapacity];
}
/**
* Constructs an empty list with an initial capacity of ten.
*/
public ArrayList() {
this(10);
}
Add方法
List接口有两个add的重载方法。第一个是在list的末尾加入元素,第二个是随机位置加入元素,其自己主动扩展数据容量的方法是ensureCapacity(int),保证大小:
/**
* 此方法在父类中已经实现。可是arralylist覆盖了事实上现。採用更加有效率的实现
*/
public boolean add(E e) {
ensureCapacity(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
/**
* 任何位置加入元素。对应的元素后移,保持链表的特性。
* 当中System.arrayCopy效率较高。
*
*/
public void add(int index, E element) {
if (index > size || index < 0)
throw new IndexOutOfBoundsException(
"Index: "+index+", Size: "+size);
ensureCapacity(size+1); // Increments modCount!!
System.arraycopy(elementData, index, elementData, index + 1,
size - index);
elementData[index] = element;
size++;
}
/**
* 保证list链表的大小。假设不足则扩容。
* 这种方法比較消耗性能。因此,假设实现能够知道或者预估AyyayList的大小,那么 * 能够在构造的时候设定合适的初始容量。 */ public void ensureCapacity(int minCapacity) { modCount++; //获取旧的数组大小 int oldCapacity = elementData.length; //比較,假设不足则扩容 if (minCapacity > oldCapacity) { Object oldData[] = elementData; int newCapacity = (oldCapacity * 3)/2 + 1; if (newCapacity < minCapacity) newCapacity = minCapacity; // 调用效率较高的arraycopy elementData = Arrays.copyOf(elementData, newCapacity); } }
Vector
实现和ArraaylIst基本一致。仅仅是在方法上加了同步操作,但须要注意其线程安全是相对的,比方一个线程进行add操作,另外一个线程迭代,肯定会出异常。
另外有个Stack继承Vector。加入了栈的相关方法。如push,和pop。
LinkedList
基于链表的顺序表。和ArrayList相比。其随机插入和删除的效率较高。由于其不须要扩容和移动元素操作。可是随机訪问效率较低(随机訪问须要从头节点遍历)。
AbstractSequentialList
LinkedList其继承于AbstractSequentialList。而当中AbstractSequentialList实现了abstractlist中的抽象方法。都是基于迭代器实现的,它同一时候把返回迭代器的方法覆盖为抽象方法。故LinkedList实现的关键在于迭代器。例如以下所看到的:
public abstract class AbstractSequentialList<E> extends AbstractList<E> {
/**
*基于迭代器实现的get
*/
public E get(int index) {
try {
return listIterator(index).next();
} catch (NoSuchElementException exc) {
throw new IndexOutOfBoundsException("Index: "+index);
}
}
/**
*基于迭代器实现的set
*/
public E set(int index, E element) {
try {
ListIterator<E> e = listIterator(index);
E oldVal = e.next();
e.set(element);
return oldVal;
} catch (NoSuchElementException exc) {
throw new IndexOutOfBoundsException("Index: "+index);
}
}
/*
基于迭代器实现的add
*/
public void add(int index, E element) {
try {
listIterator(index).add(element);
} catch (NoSuchElementException exc) {
throw new IndexOutOfBoundsException("Index: "+index);
}
}
/**
* 基于迭代器实现的remove
*
*/
public E remove(int index) {
try {
ListIterator<E> e = listIterator(index);
E outCast = e.next();
e.remove();
return outCast;
} catch (NoSuchElementException exc) {
throw new IndexOutOfBoundsException("Index: "+index);
}
}
public Iterator<E> iterator() {
return listIterator();
}
/**
* Returns a list iterator over the elements in this list (in proper
* sequence).
*
*/
public abstract ListIterator<E> listIterator(int index);
}
LinkedList
Linkedlist除了实现了List接口,还实现了队列的相关接口,这里略过不提。由listiterator接口,可知linkedList也是一个可双向遍历的顺序表。
仅仅需研究每一个链表节点的结构和迭代器实现。
链表节点例如以下所看到的,是一个静态内部类:
private static class Entry<E> {
E element;
Entry<E> next;
Entry<E> previous;
Entry(E element, Entry<E> next, Entry<E> previous) {
this.element = element;
this.next = next;
this.previous = previous;
}
}
其迭代器实现是一个内部类。接下来以其add操作说明,其它类似:
private class ListItr implements ListIterator<E> {
private Entry<E> lastReturned = header;
private Entry<E> next;
private int nextIndex;
private int expectedModCount = modCount;
/**
* 构造的时候採用一个优化技巧,依据index决定从前还是从后遍历。
*/ ListItr(int index) { if (index < 0 || index > size) throw new IndexOutOfBoundsException("Index: "+index+ ", Size: "+size); if (index < (size >> 1)) { next = header.next; for (nextIndex=0; nextIndex<index; nextIndex++) next = next.next; } else { next = header; for (nextIndex=size; nextIndex>index; nextIndex--) next = next.previous; } } /* * 链表插入,在构造此迭代器的时候,index就是插入的位置,故直接插入元素就可以 * * addbefor是ArrayList的方法,就是链表插入,指针改变的过程,这里不赘述。 */ public void add(E e) { checkForComodification(); lastReturned = header; addBefore(e, next); nextIndex++; expectedModCount++; } final void checkForComodification() { if (modCount != expectedModCount) throw new ConcurrentModificationException(); } } /** *AbstractSequentialList的方法,随机插入。
首先构造从index開始的迭代器 */ public void add(int index, E element) { try { listIterator(index).add(element); } catch (NoSuchElementException exc) { throw new IndexOutOfBoundsException("Index: "+index); } }
CopyOnWriteArrayList
CopyOnWriteArrayList是java并发包中的一个工具类,在jdk1.5的时候被引入,单独一个它的内容足以写一篇文章,故这里不再展开。仅仅是简要对其说明。
其基本思想从名称中能够看出,当进行写操作的时候。先复制复制出一个副本,写操作对副本进行。
读和遍历操作发生在操作发生的那一刻存在的副本上。
写操作的时候枷锁。读操作的时候不加锁。并通过java内存模型的语义,进行优雅的设计。
CopyOnWrite并发容器用于读多写少的并发场景。CopyOnWrite容器仅仅能保证数据的终于一致性。不能保证数据的实时一致性。所以假设你希望写入的的数据。立即能读到。请不要使用CopyOnWrite容器。
详细能够參考一下文章: