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)); // } } }