Java中的List继承自Collection接口。List是有序的Collection,使用此接口能够精确的控制每个元素插入的位置。用户能够使用索引(元素在List中的位置,类似于数组下标)来访问List中的元素,这类似于Java的数组。跟Set集合不同的是,List允许有重复元素。对于满足e1.equals(e2)条件的e1与e2对象元素,可以同时存在于List集合中。当然,也有List的实现类不允许重复元素的存在。除了具有Collection接口必备的iterator()方法外,List还提供一个listIterator()方法,返回一个ListIterator接口,和标准的Iterator接口相比,ListIterator多了一些add()之类的方法,允许添加,删除,设定元素,还能向前或向后遍历。
实现List接口的常用类有LinkedList,ArrayList,这里模拟两种List的特点进行实现从而加深理解。
一、 双向链表LinkedSequence:
a) 私有成员变量:
private Entry head;
private int cnt;
i. head为Entry型的每个LinkedSequence头指针,通过它能够访问该对象的所有内部数据,注意head不存储数据,只是用作头节点标记。
Entry结构具体在下面介绍。
ii. cnt记录每个LinkedSequence内部的成员个数,为了方便内部类的使用,创建了私有方法setSize()对齐进行修改。
b) 私有内部静态类:
private static class Entry {
SequenceItem si;
Entry befo;
Entry next;
Entry(SequenceItem si, Entry befo, Entry next) {
this.si = si;
this.befo = befo;
this.next = next;
}
}
每个实体含有一个SequenceItem型的私有成员变量si,指向前一个Entry的引用befo,指向下一个Entry的引用next。
c) 构造函数:
LinkedSequence() {
head = new Entry(null, null, null);
head.next = head;
head.befo = head;
cnt = 0;
}
i. 将head初始化,所有数据成员均为空;
ii. 将head的next和befo均标记为自己;
iii. cnt计数清零。
d) 公有方法:
i. public void add(SequenceItem item)链表尾段添加item元素:
创建一个Entry型的tmp,它的si为item,befo即head的befo(head的befo指向链表中的最后一个Entry),next即head。
将原来的末端元素的next标记为tmp,将head的befo指向新的末端元素tmp。
计数器cnt加一。
ii. public SequenceItem get(int i)获取链表中第i个元素,起始下标为0:
如果索引i超出[0, cnt),则抛出越界异常。
创建Entry e指向第一个元素,通过next向后迭代i次,返回此时的e.si。
iii. public void remove(SequenceItem item)删除链表中的第一个item元素:
如果item为空,不做处理;
创建Entry e指向第一个元素,根据e扫描链表,如果有一个e的si与item相等,创建一个Entry tmp指向此时的e,将e的前驱节点的next标记为e的后继节点,将e的后继节点的befo标记为e的前驱结点。
将tmp的befo、next和si均标记为空,计数器cnt减一。
如果遍历整个列表结束后没有找到,则抛出NoSuchElementException异常。
iv. public boolean contains(SequenceItem item)判断链表中是否含有item元素:
与remove()操作类似,只不过返回true和false不做敖述。
v. public int size()返回链表的长度:返回cnt。
vi. public boolean isEmpty()判断链表是否为空:
返回cnt==0?
vii. public SequenceItem[] toArray()将链表转化为数组:
创建一个大小为cnt的SequenceItem数组ans,依次将链表中每个节点e的si放入ans即可。
viii. public boolean equals(Sequence seq)判断两个链表元素是否相对:
如果两个Sequence的大小不等,则不再进行比较,直接返回false。
否则依次比较两个sequence的全部元素,一旦含有一个不同则返回false。
ix. public String toString()将链表转化为字符串:
与toArray类似,只不过将原来的ans用string存储,并在放入结束后返回Arrays.toString(ans)。
x. public SeqIterator iterator()正向迭代器:
返回一个SeqIterator的内部匿名类对象,含有私有成员变量index,记录当前访问元素的相对位置,初始化为0;Entry e记录当前将要访问的节点,初始化为链表的第一个元素。
public boolean hasNext():返回index是否小于LinkedSequence.this.size()。
public SequenceItem next():返回当前e.si,index加1,e指向e.next。
public void remove():只有当index大于0时,才可以将前一个元素删除,删除操作同remove()。注意删除后需要将index减一!
xi. public SeqIterator reverseIterator()反向迭代器:
返回一个SeqIterator的内部匿名类对象,含有私有成员变量index,记录当前访问元素的相对位置,初始化为size()-1;Entry e记录当前将要访问的节点,初始化为链表的最后一个元素。
public boolean hasNext():返回index是否大于-1。
public SequenceItem next():返回当前e.si,index减1,e指向e.befo。
public void remove():只有当index小于size()-1时,才可以将后一个元素删除,删除操作同remove()。注意此时无需修改index。
xii. public BiSeqIterator biIterator()双向迭代器:
即前两种迭代器的综合。
二、 变长数组ArraySequence:
a) 私有成员变量:
private SequenceItem[] sequenceItemArray = new SequenceItem[2];
private int cnt = 0;
i. sequenceItemArray为每个ArraySequence数据存储数组。
ii. cnt记录每个ArraySequence内部的成员个数,与数组大小不同。
b) 构造函数:
ArraySequence() {
sequenceItemArray = new SequenceItem[2];
cnt = 0;
}
i. 将sequenceItemArray初始化大小为2;
ii. cnt计数清零。
c) 公有方法:基本与LinkedSequence类似,主要区别在于add()和remove()操作。
i. public void add(SequenceItem item)数组中添加item元素:
首先确保sequenceItemArray的大小可以装下cnt+1个元素,如果超出sequenceItemArray的大小,需要将当前数组大小扩充为原来的一倍,再将item放入,计数器cnt加一。
ii. public void remove(SequenceItem item)删除链表中的第一个item元素:
首先在sequenceItemArray中寻找item的下标,记为i,则后面的cnt-i-1个元素都需要前移,之后将cnt-1下标处置为空。
以上完成了删除操作,需要对容器的大小重新确定,如果当前cnt不足sequenceItemArray长度的1/4,则可将sequenceItemArray的长度缩短一半,方法类似于扩张的方法。
三、 性能比较:
四、源码:
Sequence接口:
import java.util.*;
interface Sequence {
void add(SequenceItem item);
SequenceItem get(int i);
void remove(SequenceItem item);
boolean contains(SequenceItem item);
int size();
boolean isEmpty();
SeqIterator iterator();
SeqIterator reverseIterator();
BiSeqIterator biIterator();
SequenceItem[] toArray();
boolean equals(Sequence seq);
String toString();
}
SeqIterator接口:
interface SeqIterator {
boolean hasNext();
SequenceItem next();
void remove();
}
数据成员SequenceItem类:
public class SequenceItem {
private String data;
public String getData() {
return data;
}
public void setData(String s) {
data = new String(s);
}
public boolean equals(SequenceItem si) {
return this.getData().equals(si.getData());
}
}
双向链表LinkedSequence:
import java.util.*;
public class LinkedSequence implements Sequence {
private Entry head;
private int cnt;
private static class Entry {
SequenceItem si;
Entry befo;
Entry next;
Entry(SequenceItem si, Entry befo, Entry next) {
this.si = si;
this.befo = befo;
this.next = next;
}
}
LinkedSequence() {
head = new Entry(null, null, null);
head.next = head;
head.befo = head;
cnt = 0;
}
public void add(SequenceItem item) {
Entry tmp = new Entry(item, head.befo, head);
tmp.befo.next = tmp;
head.befo = tmp;
cnt++;
}
public SequenceItem get(int i) {
if (i >= cnt || i < 0) {
throw new IndexOutOfBoundsException();
}
Entry e = head.next;
for (int j = 0; j < i; j++) {
e = e.next;
}
return e.si;
}
public void remove(SequenceItem item) {
if (item == null) {
return;
}
Entry e = head.next;
for (int i = 0; i < cnt; i++) {
if (item.equals(e.si)) {
Entry tmp = e;
tmp.befo.next = tmp.next;
tmp.next.befo = tmp.befo;
tmp.befo = null;
tmp.next = null;
tmp.si = null;
cnt--;
return;
}
e = e.next;
}
throw new NoSuchElementException();
}
public boolean contains(SequenceItem item) {
if (item == null) {
return false;
}
Entry e = head.next;
for (int i = 0; i < cnt; i++) {
if (item.equals(e.si)) {
return true;
}
e = e.next;
}
return false;
}
public int size() {
return this.cnt;
}
private void setSize(int cnt) { // use for inner class to modify cnt
this.cnt = cnt;
// System.out.println("cnt:"+this.cnt);
}
public boolean isEmpty() {
return this.cnt==0 ? true:false;
}
public SeqIterator iterator() {
return new SeqIterator() {
private int index = 0;
private Entry e = head.next;
public boolean hasNext() {
return index<LinkedSequence.this.size();
}
public SequenceItem next() {
SequenceItem res = e.si;
e = e.next;
index++;
return res;
}
public void remove() {
if (index > 0) {
Entry tmp = e.befo;
tmp.befo.next = tmp.next;
tmp.next.befo = tmp.befo;
tmp.befo = null;
tmp.next = null;
tmp.si = null;
index--;
LinkedSequence.this.setSize(LinkedSequence.this.size()-1);
}
}
};
}
public SeqIterator reverseIterator() {
return new SeqIterator() {
private int index = LinkedSequence.this.size()-1;
private Entry e = head.befo;
public boolean hasNext() {
return index>-1;
}
public SequenceItem next() {
SequenceItem res = e.si;
e = e.befo;
index--;
return res;
}
public void remove() {
if (index < LinkedSequence.this.size()-1) {
Entry tmp = e.next;
tmp.befo.next = tmp.next;
tmp.next.befo = tmp.befo;
tmp.befo = null;
tmp.next = null;
tmp.si = null;
LinkedSequence.this.setSize(LinkedSequence.this.size()-1);
}
}
};
}
public BiSeqIterator biIterator() {
return new BiSeqIterator() {
private int index = 0;
private Entry e = head.next;
public boolean hasNext() {
return index<LinkedSequence.this.size();
}
public SequenceItem next() {
SequenceItem res = e.si;
e = e.next;
index++;
return res;
}
public void remove() {
if (index > 0) {
Entry tmp = e.befo;
tmp.befo.next = tmp.next;
tmp.next.befo = tmp.befo;
tmp.befo = null;
tmp.next = null;
tmp.si = null;
index--;
LinkedSequence.this.setSize(LinkedSequence.this.size()-1);
}
}
public boolean hasPrevious() {
// System.out.println(index);
return index>-1;
}
public SequenceItem previous() {
SequenceItem res = e.si;
e = e.befo;
index--;
return res;
}
};
}
public SequenceItem[] toArray() {
SequenceItem[] ans = new SequenceItem[cnt];
Entry e = head.next;
for (int i = 0; i < cnt; i++) {
ans[i] = e.si;
e = e.next;
}
return ans;
}
public boolean equals(Sequence seq) { //?
if (this.size() != seq.size()) {
return false;
}
Entry e1 = this.head.next;
for (int i = 0; i < cnt; i++) {
if (!e1.si.equals(seq.get(i))) {
return false;
}
e1 = e1.next;
}
return true;
}
public String toString() {
String[] s = new String[cnt];
Entry e = head.next;
for (int i = 0; i < cnt; i++) {
s[i] = e.si.getData();
e = e.next;
}
return Arrays.toString(s);
}
}
变长数组ArraySequence:
import java.util.*;
public class ArraySequence implements Sequence {
private SequenceItem[] sequenceItemArray;
private int cnt;
ArraySequence() {
sequenceItemArray = new SequenceItem[2];
cnt = 0;
}
private void ensureCapacity(int currCapacity) {
if (currCapacity >= sequenceItemArray.length) {
int newCapacity = sequenceItemArray.length<<1;
sequenceItemArray = Arrays.copyOf(sequenceItemArray, newCapacity);
}
else if (currCapacity < sequenceItemArray.length/4) {
int newCapacity = sequenceItemArray.length>>1;
sequenceItemArray = Arrays.copyOf(sequenceItemArray, newCapacity);
}
}
public void add(SequenceItem item) {
ensureCapacity(cnt+1);
sequenceItemArray[cnt++] = item;
}
public SequenceItem get(int i) {
return sequenceItemArray[i];
}
public void remove(SequenceItem item) {
if (item != null) {
for (int i = 0; i < cnt; i++) {
if (item.equals(sequenceItemArray[i])) {
int numMoved = cnt-i-1;
if (numMoved > 0) {
System.arraycopy(sequenceItemArray, i+1, sequenceItemArray, i, numMoved);
}
sequenceItemArray[--cnt] = null;
ensureCapacity(cnt);
return;
}
}
}
}
public boolean contains(SequenceItem item) {
for (int i = 0; i < cnt; i++) {
if (item.equals(sequenceItemArray[i])) {
return true;
}
}
return false;
}
public int size() {
// System.out.println(cnt+","+sequenceItemArray.length);
return cnt;
}
public boolean isEmpty() {
return cnt==0 ? true:false;
}
public SeqIterator iterator() {
return new SeqIterator() {
private int index = 0;
public boolean hasNext() {
return index<cnt;
}
public SequenceItem next() {
return sequenceItemArray[index++];
}
public void remove() {
if (index > 0) {
ArraySequence.this.remove(ArraySequence.this.get(--index));
}
}
};
}
public SeqIterator reverseIterator() {
return new SeqIterator() {
private int index = ArraySequence.this.size()-1;
public boolean hasNext() {
return index>-1;
}
public SequenceItem next() {
return ArraySequence.this.get(index--);
}
public void remove() {
ArraySequence.this.remove(ArraySequence.this.get(index+1));
if (index >= ArraySequence.this.size()) {
index = ArraySequence.this.size()-1;
}
}
};
}
public BiSeqIterator biIterator() {
return new BiSeqIterator() {
private int index = 0;
public boolean hasNext() {
return index<ArraySequence.this.size();
}
public SequenceItem next() {
return ArraySequence.this.get(index++);
}
public void remove() {
if (index > 0) {
ArraySequence.this.remove(ArraySequence.this.get(--index));
}
}
public boolean hasPrevious() {
return index>0;
}
public SequenceItem previous() {
return ArraySequence.this.get(--index);
}
};
}
public SequenceItem[] toArray() {
return sequenceItemArray;
}
public boolean equals(Sequence seq) {
if (this.size() !=seq.size()) {
return false;
}
for (int i = 0; i < this.size(); i++) {
if (!this.get(i).equals(seq.get(i))) {
return false;
}
}
return true;
}
public String toString() {
String[] s = new String[cnt];
for (int i = 0; i < cnt; i++) {
s[i] = sequenceItemArray[i].getData();
}
return Arrays.toString(s);
}
}
附测试程序test.java:
import java.util.*;
public class test {
public static void main(String[] args) {
// Sequence test = new LinkedSequence();
Sequence test = new ArraySequence();
SequenceItem tmp = new SequenceItem();
// Test add(), size()
for (int cnt = 1000; cnt <= 1000000; cnt *= 10) {
int testTime = 10;
double ans = 0;
double[] res = new double[testTime];
while (testTime > 0) {
// Date begin = new Date();
for (int i = 0; i < cnt; i++) {
tmp.setData(String.valueOf(i));
test.add(tmp);
// System.out.println(test.size());
}
// Date end = new Date();
// Date begin = new Date();
// for (int i = 0; i < cnt; i++) {
// tmp = test.get(0);
// // System.out.println(tmp.getData());
// test.remove(tmp);
// }
// Date end = new Date();
// Date begin = new Date();
// for (int i = 0; i < cnt; i++) {
// tmp = test.get(i);
// // System.out.println(tmp.getData());
// }
// Date end = new Date();
SeqIterator si = test.iterator();
Date begin = new Date();
while (si.hasNext()) {
si.next();
}
Date end = new Date();
res[--testTime] = ((double)end.getTime()-begin.getTime())/1000;
ans += res[testTime];
}
System.out.println(Arrays.toString(res));
System.out.println(ans);
}
// Test add(), get()
// for (int i = 0; i < 10; i++) {
// SequenceItem tmp = new SequenceItem();
// tmp.setData(String.valueOf(i));
// test.add(tmp);
// System.out.println(test.get(i).getData());
// }
// Test iterator(), reverseIterator()
// SeqIterator si = test.iterator();
// while (si.hasNext()) {
// System.out.println(si.next().getData());
// si.remove();
// }
// System.out.println(test.isEmpty());
// si = test.reverseIterator();
// while (si.hasNext()) {
// System.out.println(si.next().getData());
// si.remove();
// }
// System.out.println(test.isEmpty());
// Test biIterator()
// BiSeqIterator bsi = test.biIterator();
// while (bsi.hasNext()) {
// System.out.println("test next:");
// System.out.println(bsi.next().getData());
// // bsi.remove();
// }
// System.out.println(test.isEmpty());
// bsi.previous();
// while (bsi.hasPrevious()) {
// System.out.println("test previous:");
// System.out.println(bsi.previous().getData());
// }
// System.out.println(test.isEmpty());
// Test toString()
// System.out.println(test);
// Test isEmpty(), get(), remove(), contains(), size()
// while (!test.isEmpty()) {
// SequenceItem tmp = test.get(0);
// System.out.println(tmp.getData());
// test.remove(tmp);
// System.out.println(test.size());
// System.out.println(test.contains(tmp));
// }
}
}