[Java]ArrayList与LinkedList的模拟实现

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));
		// }
	}
}
时间: 2024-10-11 22:31:04

[Java]ArrayList与LinkedList的模拟实现的相关文章

Java ArrayList 和 LinkedList区别

ArrayList是实现了基于动态数组的数据结构,LinkedList基于链表的数据结构. 1.对ArrayList和LinkedList而言,在列表末尾增加一个元素所花的开销都是固定的.对ArrayList而言,主要是在内部数组中增加一项,指向所添加的元素,偶尔可能会导致对数组重新进行分配:而对LinkedList而言,这个开销是统一的,分配一个内部Entry对象. 2.在ArrayList的中间插入或删除一个元素意味着这个列表中剩余的元素都会被移动:而在LinkedList的中间插入或删除一

JAVA ArrayList VS LinkedList 对比之一-背景调查

API 地址:http://docs.oracle.com/javase/6/docs/api/ 涉及概念之间的关系如下: 图解: List 是一个接口,它继承于Collection的接口.它代表着有序的队列.它允许重复的元素. AbstractList 是一个抽象类,它继承于AbstractCollection.AbstractList实现List接口中除size().get(int location)之外的函数. AbstractSequentialList 是一个抽象类,它继承于Abstr

java ArrayList与LinkedList知识点

一 ArrayList 1.  arraylist里面是通过数组实现的 /** * 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; /** * The size of the Array

Java中ArrayList和LinkedList区别

一般大家都知道ArrayList和LinkedList的大致区别:      1.ArrayList是实现了基于动态数组的数据结构,LinkedList基于链表的数据结构.      2.对于随机访问get和set,ArrayList觉得优于LinkedList,因为LinkedList要移动指针.      3.对于新增和删除操作add和remove,LinedList比较占优势,因为ArrayList要移动数据. ArrayList和LinkedList是两个集合类,用于存储一系列的对象引用

java的List接口的实现类 ArrayList,LinkedList,Vector 的区别

Java的List接口有3个实现类,分别是ArrayList.LinkedList.Vector,他们用于存放多个元素,维护元素的次序,而且允许元素重复. 3个具体实现类的区别如下: 1. ArrayList是最常用的List实现类,内部是通过数组实现的,它允许对元素进行快速随机访问.数组的缺点是每个元素之间不能有间隔,当数组大小不满足时需要增加存储能力,就要将已经有数组的数据复制到新的存储空间中.当从ArrayList的中间位置插入或者删除元素时,需要对数组进行复制.移动.代价比较高.因此,它

Java中arraylist和linkedlist源代码分析与性能比較

Java中arraylist和linkedlist源代码分析与性能比較 1,简单介绍 在java开发中比較经常使用的数据结构是arraylist和linkedlist,本文主要从源代码角度分析arraylist和linkedlist的性能. 2,arraylist源代码分析 Arraylist底层的数据结构是一个对象数组.有一个size的成员变量标记数组中元素的个数,例如以下图: * The array buffer into which the elements of the ArrayLis

JAVA集合类之ArrayList和LinkedList性能比较

关于ArrayList和LinkedList这两个集合类的性能,网上很多文章表示:ArrayList的插入性能要比LinkedList差.今天突然想测试下,这个结论是否准确. 编写了如下代码: import java.util.ArrayList; import java.util.LinkedList; import java.util.List; public class Demo { public static void main(String[] args) { int count = 

java学习笔记--类ArrayList和LinkedList的实现

在集合Collection下的List中有两个实现使用的很频繁,一个是ArrayList,另一个是LinkedList,在学习中肯定都会有这样的疑问:什么时候适合使用ArrayList,什么时候用LinkedList?这时,我们就需要了解ArrayList和LinkedList的底层的实现,下面,为了更好的了解它们具体是怎样实现的,我们来写自己的ArrayList 和LinkedList. ArrayList底层是基于数组实现的,数组在内存中是存储在连续的存储单元中,在数据查找的时候比较快,适用

Java ArrayList、Vector和LinkedList等的差别与用法(转)

Java ArrayList.Vector和LinkedList等的差别与用法(转) ArrayList 和Vector是采取数组体式格式存储数据,此数组元素数大于实际存储的数据以便增长和插入元素,都容许直接序号索引元素,然则插入数据要设计到数组元素移动等内存操纵,所以索引数据快插入数据慢,Vector因为应用了synchronized办法(线程安然)所以机能上比ArrayList要差,LinkedList应用双向链表实现存储,按序号索引数据须要进行向前或向后遍历,然则插入数据时只须要记录本项的